28.01
2021
Tanto a linguagem C, como a linguagem C++, contam com um conjunto de macros pré-definidas. Elas estão resolvidas em tempo de pré-processamento, portanto antes mesmo da compilação, e podem ser muito úteis. Vamos conhecê-las e estudar utilidades para o dia-a-dia.
Pré-Compilação
O processo de geração de executáveis em C++ passam basicamente por três passos. No primeiro deles as diretivas de pré-compilação são substituídas. Diretivas como #define, #include, #ifdef, etc, são devidamente resolvidas e substituídas, sejam localmente, sejam no resto do código. Depois das substituições o texto resultante é passado ao compilador e em seguida para o linker.
Macros são diretivas de pré-compilação que são substituídas no código durante o processo de pré-compilação. Portanto, nome de arquivo, número de linha, data e hora, são informações referentes ao tempo de pré-compilação.
__FILE__, __LINE__ e __func__
As macros __FILE__ e __LINE__ são resolvidas como o nome e a linha corrente do arquivo, respectivamente, novamente em tempo de pré-compilação. __func__ é resolvida como o nome da função, porém é padrão somente a partir de C99.
No caso de pré-compiladores externos, como no caso de Oracle Pro*C, o número da linha será referente ao arquivo .c gerado pelo Pro*C e não referente ao arquivo .pc gerado pelo desenvolvedor. Isso vale para qualquer outro pré-compilador.
Vamos a um exemplo prático:
fprintf(stderr, "Falha de alocacao de memoria em %s:%d (%s)\n", __FILE__, __LINE__, __func__);
O código acima irá escreve algo mais ou menos assim em stderr:
Falha de alocacao de memoria em macros.c:5 (readconfig)
A utilização do nome do arquivo, da linha do arquivo e do nome da função é muito útil no processo de depuração de erros, indicando claramente aonde o problema foi encontrado. Para erros fatais e irrecuperáveis, principalmente fora do ambiente produtivo, é uma ótima ferramenta para indicar aonde o erro aconteceu.
Há algum tempo no Lab C++ um exemplo de Trace de Alocação em C ANSI, utilizando __LINE__ e __FILE__. Tem um snippet de código muito útil para quem quer evitar vazamentos de memória.
__DATE__ e __TIME__
As macros __DATE__ e __TIME__ são resolvidas como a data e hora da execução do pré-processamento. __DATE__ é resolvida por um const char* no formato MMM DD YYYY, com o mês com três caracteres em inglês, dia com dois dígitos, completado com espaço, e ano com quatro dígitos. __TIME__ é resolvida por um const char* no formato HH:MM:SS, com o formato de horas de 24 horas.
Vamos a um exemplo:
printf("Macro Test v2.1. Build: %s\n", __DATE__);
Que retorna a saída:
Macro Test v2.1. Build: Jan 26 2021
Que pode ser muito útil para obter a data de compilação do binário em questão. Prática que facilita na definição de qual versão exatamente o programa estava sendo executado em ambiente produtivo.
Através desta data é possível efetuar buscas das versões afetadas no sistema de gestão de código fonte, por exemplo no git, subversion, cvs, etc, reduzindo o tempo necessário para determinar a causa raíz de um problema.
__STDC__, __STDC_VERSION__ e __cplusplus
__STDC__ resolve como 1, numérico, caso o compilador seja padrão ISO C. Já __STDC__VERSION__ somente estará definido caso o compilador esteja no modo C ANSI, e neste caso conterá a versão do build do compilador. O mesmo se aplica com a macro __cplusplus para C++.
Na prática são ótimas ferramentas para entender se o compilador está no modo C ANSI ou C++.
#ifdef __STDC_VERSION__
#include <stdio.h>
#endif
#ifdef __cplusplus
#include <iostream>
#endif
int main() {
#ifdef __STDC_VERSION__
printf("C ANSI compiler version: %d", __STDC_VERSION__);
#endif
#ifdef __cplusplus
std::cout << "C++ compiler version: " << __cplusplus << std::endl;
#endif
}
O código acima detecta se o compilador usado para fazer a pré-compilação está no modo C, ou C++. Note que o mesmo compilador pode entregar resultados diferentes.
Conhecer o compilador facilita no desenvolvimento de bibliotecas que podem ser compiladas para diversos usos, facilitando na redução da quantidade de código, e reduzindo o custo total de manutenção.