Skip to content

Commit f6ecc5a

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

File tree

2 files changed

+206
-1
lines changed

2 files changed

+206
-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,201 @@
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+
52+
Macros também podem obter o nome da função e número da linha em que foram usados.
53+
54+
Por exemplo:
55+
56+
```c++:line-numbers=5
57+
#define debug(x) \
58+
cout << "Linha " << __LINE__ \
59+
<< ", função " << __FUNCTION__ << ": " \
60+
<< #x << " = " << x << endl
61+
62+
void func(string x) { debug(x); }
63+
64+
int main() {
65+
func("teste");
66+
debug(33 + 36);
67+
}
68+
```
69+
70+
Tem como saída:
71+
72+
```bash:no-line-numbers
73+
Linha 10, função func: x = teste
74+
Linha 14, função main: 80 + 10 = 90
75+
```
76+
77+
Porém, como visto em todos esses exemplos, apenas uma variável está sendo debuggada
78+
por vez. Podemos melhorar?
79+
80+
## Parte 2: Templates também ajudam! {#templates}
81+
82+
Templates em C++ são ferramentas complexas e poderosas capazes de fazer uma função
83+
poder receber vários tipos de dados diferentes, o que será fundamental pros códigos
84+
a seguir.
85+
86+
Exemplo simples:
87+
88+
```c++
89+
void printer() { cout << endl; }
90+
template <typename H, typename... T> void printer(H val, T... args) {
91+
cout << val << ", ";
92+
printer(args...);
93+
}
94+
95+
int main() {
96+
printer("a", "b", true, false, 10, 0.5);
97+
return 0;
98+
}
99+
```
100+
101+
Terá `a, b, 1, 0, 10, 0.5,` como saída.
102+
103+
Agora podemos debuggar várias variáveis de uma vez só.... Mas e os nomes das
104+
variáveis, que apareciam nos macros? E o número da linha?
105+
106+
## Parte 3: Macros + Templates, união sinistra {#templates-e-macros}
107+
108+
Juntando o ~in~útil ao ~des~agradável, podemos criar um macro capaz de receber
109+
infinitas expressões, e usá-lo junto do template anterior:
110+
111+
```c++
112+
#define debug(...) ({ \
113+
cout << "Linha " <<__LINE__ << ": "; \
114+
printer(__VA_ARGS__); \
115+
})
116+
```
117+
118+
E assim obtemos de volta a informação sobre a linha onde isso foi executado!
119+
120+
..... Mas e os nomes das variáveis?
121+
122+
É ligeiramente mais complicado, mas nada demais. Considerando que
123+
`#define nome(x) #x` retorna a expressão `x` como uma string, também podemos aplicar
124+
essa idéia á macros com infinitos argumentos:
125+
126+
```c++
127+
#define nomes(...) #__VA_ARGS__
128+
int x = 10;
129+
string str = "sim";
130+
string dados = nomes(x, str, true, 30 >> 1);
131+
```
132+
133+
Nesse trecho de código, a variável `dados` assumirá o valor `"x, str, true, 30 >> 1"`.
134+
Ou seja, só precisamos printar a string antes de cada vírgula,
135+
e depois mostrar o valor real.
136+
137+
Implementação:
138+
139+
```c++
140+
void print_name(string &s) {
141+
// Printa caractere a caractere, até achar uma vírgula
142+
do {
143+
cout << s[0];
144+
s = s.substr(1);
145+
} while (s.size() && s[0] != ',');
146+
cout << " = ";
147+
}
148+
149+
void printer(string s) { cout << endl; }
150+
template <typename V, typename... A> void printer(string s, V val, A... args) {
151+
print_name(s); // Mostra o nome da variável
152+
cout << val; // Mostra o valor da variável
153+
printer(s, args...); // Segue recursivamente
154+
}
155+
156+
#define debug(...) ({ \
157+
cout << "Linha " << __LINE__ << ": "; \
158+
printer(#__VA_ARGS__, __VA_ARGS__); \
159+
})
160+
```
161+
162+
Agora sim, você terá a linha de execução e o nome das variáveis!
163+
164+
Porém, ainda existem limitações: Esse código não é capaz de debuggar estruturas
165+
como vetores, sets, maps, e etc.
166+
Suportar essas estruturas aumentam bastante a complexidade, e para algo realmente
167+
completo, recomendamos ler [este post no Codeforces](https://codeforces.com/blog/entry/125435)
168+
ou os arquivos do [repositório disponibilizado no post](https://github.com/Anshul-Johri-1/Debug-Template/).
169+
170+
De toda forma, para adicionar o suporte a vetores de forma simples no código anterior,
171+
é só adicionar essa função logo acima da função `printer` anterior:
172+
173+
```c++
174+
template <typename H, typename... A>
175+
void printer(string s, vector<H> &vec, A... args) {
176+
print_name(s);
177+
cout << "{";
178+
for (int len = vec.size(), i = 0; i < len; ++i) {
179+
cout << " " << vec[i];
180+
if (i + 1 != len)
181+
cout << ",";
182+
}
183+
cout << " }";
184+
printer(s, args...);
185+
}
186+
```
187+
188+
## Desativando o debug
189+
190+
Novamente, macros te ajudarão. Você pode escrever algo do tipo:
191+
192+
```c++
193+
#define DEBUG // [!code highlight]
194+
#ifdef DEBUG
195+
#define debug(...) printer(#__VA_ARGS__, __VA_ARGS__);
196+
#else
197+
#define debug(...) 0
198+
#endif
199+
```
200+
201+
Assim, para desativar o debug no código INTEIRO, é só comentar a linha destacada.

0 commit comments

Comments
 (0)