Número da linha C / C ++

110

Para fins de depuração, posso obter o número da linha em compiladores C / C ++? (forma padrão ou formas específicas para certos compiladores)

por exemplo

if(!Logical)
    printf("Not logical value at line number %d \n",LineNumber);
    // How to get LineNumber without writing it by my hand?(dynamic compilation)
Betamoo
fonte
17
@Lucas: Alguns de nós preferem não mexer com depuradores. Este tipo de "declaração de afirmação de pobre" às ​​vezes é mais claro porque é uma parte permanente do código e documentação duradoura de coisas que deveriam ser verdadeiras sobre o estado da computação.
S.Lott
13
@Lucas: Os depuradores também são menos úteis para problemas intermitentes em programas de longa execução ou para coletar informações sobre problemas em software implantado em sites de clientes. Nestes casos, a única opção é o programa registrar o máximo possível de informações sobre o estado do programa, para análise posterior.
KeithB de
1
@Lucas E os depuradores não funcionam tão bem em alguns sistemas embarcados para obter essas informações.
George Stocker

Respostas:

180

Você deve usar a macro do pré-processador __LINE__e __FILE__. Eles são macros predefinidas e fazem parte do padrão C / C ++. Durante o pré-processamento, eles são substituídos respectivamente por uma string constante contendo um número inteiro que representa o número da linha atual e pelo nome do arquivo atual.

Outras variáveis ​​de pré-processador:

  • __func__: nome da função (faz parte do C99 , nem todos os compiladores C ++ o suportam)
  • __DATE__ : uma string no formato "Mmm dd aaaa"
  • __TIME__ : uma string no formato "hh: mm: ss"

Seu código será:

if(!Logical)
  printf("Not logical value at line number %d in file %s\n", __LINE__, __FILE__);
Julien Hoarau
fonte
2
C99 usa __func__ em vez de __FUNCTION__, que AFAIK está parcialmente obsoleto. A diferença pode quebrar seu código, porque __func__ não pode ser usado para concatenação de string constante do C.
Joseph Quinsey
1
Referência do manual do GCC: "__FUNCTION__ e __PRETTY_FUNCTION__ foram tratados como literais de string; eles podem ser usados ​​para inicializar matrizes de caracteres e podem ser concatenados com outros literais de string. GCC 3.4 e posterior os trata como variáveis, como __func__. Em C ++, __FUNCTION__ e __PRETTY_FUNCTION__ sempre foram variáveis. "
Joseph Quinsey
Existe uma maneira de obter o número da linha como uma string, igual ao nome do arquivo? Gostaria que o pré-processador me fornecesse, por exemplo, a string literal "22" em vez do inteiro 22.
sep332
1
@ sep332 Sim, mas o cpp é uma besta estranha, então deve ser feito em duas etapas com argumentos macro. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Consulte c-faq.com/ansi/stringize.html
Rasmus Kaj
1
Dizendo estritamente, __func__não é uma macro, é uma variável declarada implicitamente.
HolyBlackCat
64

Como parte do padrão C ++, existem algumas macros predefinidas que você pode usar. A seção 16.8 do padrão C ++ define, entre outras coisas, a __LINE__macro.

__LINE__: O número da linha da linha de origem atual (uma constante decimal).
__FILE__: O nome presumido do arquivo de origem (uma cadeia de caracteres literal).
__DATE__: A data de tradução do arquivo de origem (uma cadeia de caracteres literal ...)
__TIME__ : A hora da tradução do arquivo de origem (uma string literal de caracteres ...)
__STDC__: Se __STDC__é predefinido
__cplusplus: O nome __cplusplusé definido com o valor 199711L quando compilar uma unidade de tradução C ++

Portanto, seu código seria:

if(!Logical)
  printf("Not logical value at line number %d \n",__LINE__);
Brian R. Bondy
fonte
19

Você pode usar uma macro com o mesmo comportamento de printf () , exceto que também inclui informações de depuração, como nome da função, classe e número da linha:

#include <cstdio>  //needed for printf
#define print(a, args...) printf("%s(%s:%d) " a,  __func__,__FILE__, __LINE__, ##args)
#define println(a, args...) print(a "\n", ##args)

Essas macros devem se comportar de forma idêntica a printf () , enquanto incluem informações semelhantes a java stacktrace. Aqui está um exemplo principal:

void exampleMethod() {
    println("printf() syntax: string = %s, int = %d", "foobar", 42);
}

int main(int argc, char** argv) {
    print("Before exampleMethod()...\n");
    exampleMethod();
    println("Success!");
}

O que resulta na seguinte saída:

main (main.cpp: 11) Antes de exampleMethod () ...
exampleMethod (main.cpp: 7) sintaxe de printf (): string = foobar, int = 42
main (main.cpp: 13) Sucesso!

Algum programador Java
fonte
para desenvolvimento c, você mudaria o #includepara<stdio.h>
phyatt
11

Use __LINE__(isto é, sublinhado duplo LINE), o pré-processador irá substituí-lo pelo número da linha na qual ele foi encontrado.

meagar
fonte
9

Check __FILE__- out e __LINE__macros

Anton
fonte
5

C ++ 20 oferece uma nova maneira de conseguir isso usando std :: source_location . Isso está atualmente acessível no gcc an clang como std::experimental::source_locationcom #include <experimental/source_location>.

O problema com macros como __LINE__é que se você deseja criar, por exemplo, uma função de registro que produza o número da linha atual junto com uma mensagem, você sempre deve passar __LINE__como um argumento de função, porque é expandido no local da chamada. Algo assim:

void log(const std::string msg) {
    std::cout << __LINE__ << " " << msg << std::endl;
}

Sempre produzirá a linha da declaração da função e não a linha de onde logfoi realmente chamada. Por outro lado, com std::source_locationvocê pode escrever algo assim:

#include <experimental/source_location>
using std::experimental::source_location;

void log(const std::string msg, const source_location loc = source_location::current())
{
    std::cout << loc.line() << " " << msg << std::endl;
}

Aqui, locé inicializado com o número da linha apontando para o local onde logfoi chamado. Você pode tentar online aqui.

senhor
fonte
4

Experimente __FILE__e __LINE__.
Você também pode achar __DATE__e __TIME__útil.
Porém, a menos que você tenha que depurar um programa no lado do cliente e, portanto, precise registrar essas informações, você deve usar a depuração normal.

Sanctus2099
fonte
Por que fui rejeitado nisso e por que os mmyers editaram minha postagem?
Sanctus2099
@ Sanctus2099: Foi editado porque o Markdown transformou seus sublinhados duplos para exibir ARQUIVO e LINHA em negrito (você não verifica a aparência da sua resposta?). Outro ponto pode ser (pelo menos me parece agora) que você deu uma resposta 1 hora depois que uma resposta já correta foi dada, então você não acrescentou nenhum valor.
Felix Kling
O sublinhado duplo é a sintaxe de marcação para negrito . Para exibir adequadamente os sublinhados duplos, você deve escapá-los (como este: \ _ \ _) ou usar crostas para marcá-los como raw code(como este: `__`). @mmyers tentou ajudar, mas escapou apenas de um dos sublinhados e, portanto, você ficou com a sintaxe de marcação para itálico . Os votos negativos são um pouco duros aqui, eu concordo.
Matt B.
Ok, eu não percebi o problema de sublinhados duplos transformando o texto em negrito e eu tive que desligar e não tive tempo de ver como minha resposta estava parecendo. Eu entendo agora. Mesmo que minha resposta tenha atrasado uma hora, ainda assim foi uma boa resposta. Não agregou nenhum valor, mas também não estava errado, então não há razão para downvote. Isso é o que você ganha por tentar ajudar ...
Sanctus2099
2
@ Sanctus2099 Algumas pessoas são rápidas em votar contra, por isso é importante ter certeza de que sua resposta está correta. Nesse caso, você postou uma resposta errada e a deixou sem edição por 4 horas. Você não tem ninguém para culpar a não ser você mesmo.
meagar
2

Para quem precisar, uma macro "FILE_LINE" para imprimir facilmente o arquivo e a linha:

#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)
Clarkttfu
fonte
1

Como também estou enfrentando esse problema agora e não posso adicionar uma resposta a uma pergunta diferente, mas também válida, feita aqui , vou fornecer uma solução de exemplo para o problema de: obter apenas o número da linha de onde a função foi chamada C ++ usando modelos.

Contexto: em C ++, pode-se usar valores inteiros sem tipo como um argumento de modelo. Isso é diferente do uso típico de tipos de dados como argumentos de modelo. Portanto, a ideia é usar esses valores inteiros para uma chamada de função.

#include <iostream>

class Test{
    public:
        template<unsigned int L>
        int test(){
            std::cout << "the function has been called at line number: " << L << std::endl;
            return 0;
        }
        int test(){ return this->test<0>(); }
};

int main(int argc, char **argv){
    Test t;
    t.test();
    t.test<__LINE__>();
    return 0;
}

Resultado:

a função foi chamada no número da linha: 0

a função foi chamada no número da linha: 16

Algo a ser mencionado aqui é que no C ++ 11 Standard é possível fornecer valores de template padrão para funções usando template. No pré C ++ 11, os valores padrão para argumentos sem tipo parecem funcionar apenas para argumentos de modelo de classe. Assim, em C ++ 11, não haveria necessidade de ter definições de função duplicadas como acima. Em C ++ 11 também é válido ter argumentos de template const char *, mas não é possível usá-los com literais como __FILE__ou __func__como mencionado aqui .

Portanto, no final, se você estiver usando C ++ ou C ++ 11, esta pode ser uma alternativa muito interessante do que usar macro para obter a linha de chamada.

John Doe
fonte
1

Use __LINE__, mas qual é o seu tipo?

LINE O número da linha presumida (dentro do arquivo de origem atual) da linha de origem atual (uma constante inteira).

Como uma constante inteira , o código geralmente pode assumir que o valor é __LINE__ <= INT_MAXe, portanto, o tipo é int.

Para impressão em C, printf()precisa o especificador Coincidindo: "%d". Esta é uma preocupação muito menor em C ++ com cout.

Preocupação pedante: Se o número da linha exceder INT_MAX1 (algo concebível com 16 bits int), espero que o compilador produza um aviso. Exemplo:

format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]

Como alternativa, o código pode forçar tipos mais amplos a evitar esses avisos.

printf("Not logical value at line number %ld\n", (long) __LINE__);
//or
#include <stdint.h>
printf("Not logical value at line number %jd\n", INTMAX_C(__LINE__));

Evitar printf()

Para evitar todas as limitações de inteiros: stringify . O código pode ser impresso diretamente sem uma printf()chamada: uma boa coisa a evitar no tratamento de erros 2 .

#define xstr(a) str(a)
#define str(a) #a

fprintf(stderr, "Not logical value at line number %s\n", xstr(__LINE__));
fputs("Not logical value at line number " xstr(__LINE__) "\n", stderr);

1 Certamente uma prática de programação ruim para ter um arquivo tão grande, mas talvez o código gerado por máquina possa ser alto.

2 Na depuração, às vezes o código simplesmente não funciona como esperado. Chamar funções complexas como *printf()pode gerar problemas em vez de simples fputs().

chux - Reintegrar Monica
fonte