Skip to content

Commit 0f8f871

Browse files
feat: Add tips for debugging variables
1 parent a5dc24f commit 0f8f871

File tree

2 files changed

+205
-1
lines changed

2 files changed

+205
-1
lines changed

vitepress/src/.vitepress/config/navigation/sidebar.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,11 @@ function sidebarLearn(): DefaultTheme.SidebarItem[] {
7474

7575
function sidebarTips(): DefaultTheme.SidebarItem[] {
7676
return [
77-
{ text: "Dicas de debug", base: "/", link: "TODO" }
77+
{
78+
text: "Debugging", base: "/tips/debug/", collapsed: true, items: [
79+
{ text: "Debuggando variáveis", link: "debugging-variables" }
80+
]
81+
}
7882
]
7983
}
8084

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Debuggando variáveis
2+
3+
## Introdução
4+
5+
Nem tudo são flores, e nem sempre seu código irá funcionar.
6+
E pior ainda, nem sempre você terá noção sobre o que está causando o problema.
7+
8+
Uma forma comum e eficiente para debuggar é mostrar os dados contidos nas variáveis
9+
do programa. Geralmente, isso é feito da seguinte forma:
10+
11+
```c++
12+
int a = 10;
13+
string b = "b";
14+
bool c = false;
15+
std::cout << "A = " << a << ", b = " << b << "c = " << c << endl;
16+
```
17+
18+
Porém, debuggar dessa forma tem várias desvantagens:
19+
20+
1. Ter que especificar o nome de cada variável manualmente é verboso e repetitivo.
21+
2. O `std::cout`, por si só, não é capaz de apresentar o valor de estruturas como
22+
vetores ou sets.
23+
3. Para desativar, é necessário manualmente remover todas as linhas com `std::cout`.
24+
4. Pode ficar bem confuso, principalmente se o código de debug for repetido
25+
em várias linhas.
26+
27+
## Parte 1: Macros para o resgate! {#macros}
28+
29+
Macros podem solucionar uma boa parte desses problemas. Como são expandidos
30+
em tempo de compilação, podem gerar código com informações essenciais.
31+
32+
Por exemplo, o macro a seguir:
33+
34+
```c++:no-line-numbers
35+
#define debug(x) cout << #x << " = " << x << endl
36+
```
37+
38+
Mostrará o nome e o valor da expressão x:
39+
40+
```c++
41+
int numero = 900;
42+
debug(numero); // saída: numero = 900
43+
debug(numero / 2); // saída: numero / 2 = 450
44+
debug(20 & 1); // saída: 20 & 1 = 0
45+
46+
string str = "explodiu"
47+
debug(str); // saída: str = explodiu
48+
debug("não " + str) // saída: "não " + str = não explodiu
49+
```
50+
51+
Macros também podem obter o nome da função e número da linha em que foram usados.
52+
53+
Por exemplo:
54+
55+
```c++:line-numbers=5
56+
#define debug(x) \
57+
cout << "Linha " << __LINE__ \
58+
<< ", função " << __FUNCTION__ << ": " \
59+
<< #x << " = " << x << endl
60+
61+
void func(string x) { debug(x); }
62+
63+
int main() {
64+
func("teste");
65+
debug(33 + 36);
66+
}
67+
```
68+
69+
Tem como saída:
70+
71+
```bash:no-line-numbers
72+
Linha 10, função func: x = teste
73+
Linha 14, função main: 33 + 36 = 69
74+
```
75+
76+
Porém, como visto em todos esses exemplos, apenas uma variável está sendo debuggada
77+
por vez. Podemos melhorar?
78+
79+
## Parte 2: Templates também ajudam! {#templates}
80+
81+
Templates em C++ são ferramentas complexas e poderosas capazes de fazer uma função
82+
poder receber vários tipos de dados diferentes, o que será fundamental pros códigos
83+
a seguir.
84+
85+
Exemplo simples:
86+
87+
```c++
88+
void printer() { cout << endl; }
89+
template <typename H, typename... T> void printer(H val, T... args) {
90+
cout << val << ", ";
91+
printer(args...);
92+
}
93+
94+
int main() {
95+
printer("a", "b", true, false, 10, 0.5);
96+
return 0;
97+
}
98+
```
99+
100+
Terá `a, b, 1, 0, 10, 0.5,` como saída.
101+
102+
Agora podemos debuggar várias variáveis de uma vez só.... Mas e os nomes das
103+
variáveis, que apareciam nos macros? E o número da linha?
104+
105+
## Parte 3: Macros + Templates, união sinistra {#templates-e-macros}
106+
107+
Juntando o ~~in~~útil ao ~~des~~agradável, podemos criar um macro capaz de receber
108+
infinitas expressões, e usá-lo junto do template anterior:
109+
110+
```c++
111+
#define debug(...) ({ \
112+
cout << "Linha " <<__LINE__ << ": "; \
113+
printer(__VA_ARGS__); \
114+
})
115+
```
116+
117+
E assim obtemos de volta a informação sobre a linha onde isso foi executado!
118+
119+
..... Mas e os nomes das variáveis?
120+
121+
É ligeiramente mais complicado, mas nada demais. Considerando que
122+
`#define nome(x) #x` retorna a expressão `x` como uma string, também podemos aplicar
123+
essa idéia á macros com infinitos argumentos:
124+
125+
```c++
126+
#define nomes(...) #__VA_ARGS__
127+
int x = 10;
128+
string str = "sim";
129+
string dados = nomes(x, str, true, 30 >> 1);
130+
```
131+
132+
Nesse trecho de código, a variável `dados` assumirá o valor `"x, str, true, 30 >> 1"`.
133+
Ou seja, só precisamos printar a string antes de cada vírgula,
134+
e depois mostrar o valor real.
135+
136+
Implementação:
137+
138+
```c++
139+
void print_name(string &s) {
140+
// Printa caractere a caractere, até achar uma vírgula
141+
do {
142+
cout << s[0];
143+
s = s.substr(1);
144+
} while (s.size() && s[0] != ',');
145+
cout << " = ";
146+
}
147+
148+
void printer(string s) { cout << endl; }
149+
template <typename V, typename... A> void printer(string s, V val, A... args) {
150+
print_name(s); // Mostra o nome da variável
151+
cout << val; // Mostra o valor da variável
152+
printer(s, args...); // Segue recursivamente
153+
}
154+
155+
#define debug(...) ({ \
156+
cout << "Linha " << __LINE__ << ": "; \
157+
printer(#__VA_ARGS__, __VA_ARGS__); \
158+
})
159+
```
160+
161+
Agora sim, você terá a linha de execução e o nome das variáveis!
162+
163+
Porém, ainda existem limitações: Esse código não é capaz de debuggar estruturas
164+
como vetores, sets, maps, e etc.
165+
Suportar essas estruturas aumentam bastante a complexidade, e para algo realmente
166+
completo, recomendamos ler [este post no Codeforces](https://codeforces.com/blog/entry/125435)
167+
ou os arquivos do [repositório disponibilizado no post](https://github.com/Anshul-Johri-1/Debug-Template/).
168+
169+
De toda forma, para adicionar o suporte a vetores de forma simples no código anterior,
170+
é só adicionar essa função logo acima da função `printer` anterior:
171+
172+
```c++
173+
template <typename H, typename... A>
174+
void printer(string s, vector<H> &vec, A... args) {
175+
print_name(s);
176+
cout << "{";
177+
for (int len = vec.size(), i = 0; i < len; ++i) {
178+
cout << " " << vec[i];
179+
if (i + 1 != len)
180+
cout << ",";
181+
}
182+
cout << " }";
183+
printer(s, args...);
184+
}
185+
```
186+
187+
## Desativando o debug
188+
189+
Novamente, macros te ajudarão. Você pode escrever algo do tipo:
190+
191+
```c++
192+
#define DEBUG // [!code highlight]
193+
#ifdef DEBUG
194+
#define debug(...) printer(#__VA_ARGS__, __VA_ARGS__);
195+
#else
196+
#define debug(...) 0
197+
#endif
198+
```
199+
200+
Assim, para desativar o debug no código INTEIRO, é só comentar a linha destacada.

0 commit comments

Comments
 (0)