Número de línea C / C ++


110

En aras de la depuración, ¿puedo obtener el número de línea en los compiladores C / C ++? (forma estándar o formas específicas para ciertos compiladores)

p.ej

if(!Logical)
    printf("Not logical value at line number %d \n",LineNumber);
    // How to get LineNumber without writing it by my hand?(dynamic compilation)

17
@Lucas: Algunos de nosotros preferimos no meternos con los depuradores. Este tipo de "afirmación de una persona pobre" es a veces más claro porque es una parte permanente del código y una documentación duradera de cosas que deberían ser ciertas sobre el estado de la computación.
S.Lott

13
@Lucas: Los depuradores tampoco son útiles para problemas intermitentes en programas de larga ejecución o para recopilar información sobre problemas en el software implementado en los sitios de los clientes. En estos casos, la única opción es que el programa registre tanta información sobre el estado del programa como sea posible, para su posterior análisis.
KeithB

1
@Lucas Y los depuradores no funcionan tan bien en algunos sistemas integrados para obtener esta información.
George Stocker

Respuestas:


180

Debería utilizar la macro del preprocesador __LINE__y __FILE__. Son macros predefinidas y forman parte del estándar C / C ++. Durante el preprocesamiento, se reemplazan respectivamente por una cadena constante que contiene un número entero que representa el número de línea actual y por el nombre del archivo actual.

Otras variables de preprocesador:

  • __func__: nombre de la función (esto es parte de C99 , no todos los compiladores de C ++ lo admiten)
  • __DATE__ : una cadena de formato "Mmm dd aaaa"
  • __TIME__ : una cadena de formato "hh: mm: ss"

Tu código será:

if(!Logical)
  printf("Not logical value at line number %d in file %s\n", __LINE__, __FILE__);

2
C99 usa __func__ en lugar de __FUNCTION__, que AFAIK está parcialmente desaprobado. La diferencia puede romper su código, porque __func__ no se puede usar para la concatenación constante de cadenas de C.
Joseph Quinsey

1
Referencia del manual de GCC: "__FUNCTION__ y __PRETTY_FUNCTION__ se trataron como cadenas literales; se podían usar para inicializar matrices de caracteres y se podían concatenar con otras cadenas literales. GCC 3.4 y luego tratarlas como variables, como __func__. En C ++, __FUNCTION__ y __PRETTY_FUNCTION__ siempre han sido variables ".
Joseph Quinsey

¿Hay alguna forma de obtener el número de línea como una cadena, igual que el nombre del archivo? Me gustaría que el preprocesador me diera, por ejemplo, la cadena literal "22" en lugar del entero 22.
sep332

1
@ sep332 Sí, pero el cpp es una bestia extraña, por lo que debe hacerse en dos pasos con argumentos macro. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Ver c-faq.com/ansi/stringize.html
Rasmus Kaj

1
Estrictamente diciendo, __func__no es una macro, es una variable declarada implícitamente.
HolyBlackCat

64

Como parte del estándar C ++, existen algunas macros predefinidas que puede utilizar. La sección 16.8 del estándar C ++ define, entre otras cosas, la __LINE__macro.

__LINE__: El número de línea de la línea fuente actual (una constante decimal).
__FILE__: El nombre presunto del archivo de origen (una cadena de caracteres literal).
__DATE__: La fecha de traducción del archivo de origen (una cadena de caracteres literal ...)
__TIME__: La hora de la traducción del archivo de origen (una cadena de caracteres literal ...)
__STDC__: Si __STDC__está predefinido
__cplusplus: El nombre __cplusplusse define con el valor 199711L cuando compilar una unidad de traducción de C ++

Entonces tu código sería:

if(!Logical)
  printf("Not logical value at line number %d \n",__LINE__);

19

Puede usar una macro con el mismo comportamiento que printf () , excepto que también incluye información de depuración como el nombre de la función, la clase y el número de línea:

#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)

Estas macros deberían comportarse de manera idéntica a printf () , mientras que incluyen información similar a la de Java stacktrace. Aquí hay un ejemplo de main:

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

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

Lo que da como resultado la siguiente salida:

main (main.cpp: 11) Antes exampleMethod () ...
exampleMethod (main.cpp: 7) printf () sintaxis: string = foobar, int = 42
main (main.cpp: 13) ¡Éxito!


para el desarrollo de c, cambiaría #includea<stdio.h>
phyatt

11

Use __LINE__(que es doble subrayado LINE doble subrayado), el preprocesador lo reemplazará con el número de línea en el que se encuentra.



5

C ++ 20 ofrece una nueva forma de lograr esto usando std :: source_location . Esto es actualmente accesible en gcc an clang como std::experimental::source_locationcon #include <experimental/source_location>.

El problema con las macros como __LINE__es que si desea crear, por ejemplo, una función de registro que muestre el número de línea actual junto con un mensaje, siempre debe pasar __LINE__como un argumento de función, porque se expande en el sitio de la llamada. Algo como esto:

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

Siempre generará la línea de la declaración de función y no la línea desde donde logse llamó realmente. Por otro lado, con std::source_locationpuedes escribir algo como esto:

#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;
}

Aquí, locse inicializa con el número de línea apuntando a la ubicación donde logse llamó. Puedes probarlo en línea aquí.


4

Prueba __FILE__y __LINE__.
También puede encontrar __DATE__y __TIME__útil.
Aunque, a menos que tenga que depurar un programa en el lado del cliente y, por lo tanto, necesite registrar esta información, debe utilizar la depuración normal.


¿Por qué fui rechazado por esto y por qué mmyers editaron mi publicación?
Sanctus2099

@ Sanctus2099: Fue editado, porque Markdown transformó sus guiones bajos dobles para mostrar FILE y LINE en negrita (¿no verifica cómo se ve su respuesta?). Otro punto podría ser (al menos me lo parece ahora) que dio una respuesta 1 hora después de que se dio una respuesta ya correcta, por lo que no agregó ningún valor.
Felix Kling

El subrayado doble es una sintaxis de marcado para negrita . Para mostrar correctamente los guiones bajos dobles, debe escapar de ellos (así: \ _ \ _) o usar raw codecomillas invertidas para marcarlos como (así: `__`). @mmyers intentó ayudar, pero solo escapó uno de los guiones bajos, por lo que se quedó con la sintaxis de marcado en cursiva . Los votos negativos son un poco duros aquí, sin embargo, estoy de acuerdo.
Matt B.

De acuerdo, no me di cuenta de lo que pasa con los subrayados dobles que convierten el texto en negrita y tuve que irme y no tuve tiempo de ver cómo se veía mi respuesta. Ahora lo entiendo. Incluso si mi respuesta llegó una hora tarde, seguía siendo una buena respuesta. No agregó ningún valor, pero tampoco estaba mal, por lo que no hay motivo para votar en contra. Eso es lo que obtienes por intentar ayudar ...
Sanctus2099

2
@ Sanctus2099 Algunas personas se apresuran a desestimar, por eso es importante asegurarse de que su respuesta sea correcta. En este caso, publicó una respuesta incorrecta y la dejó sin editar durante 4 horas. No tienes a nadie a quien culpar más que a ti mismo.
meagar

1

Dado que ahora también estoy enfrentando este problema y no puedo agregar una respuesta a una pregunta diferente pero también válida hecha aquí , proporcionaré una solución de ejemplo para el problema de: obtener solo el número de línea donde se ha llamado a la función C ++ usando plantillas.

Antecedentes: en C ++ se pueden utilizar valores enteros que no sean de tipo como argumento de plantilla. Esto es diferente al uso típico de tipos de datos como argumentos de plantilla. Entonces, la idea es usar tales valores enteros para una llamada de función.

#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;
}

Salida:

la función ha sido llamada en el número de línea: 0

la función ha sido llamada en el número de línea: 16

Una cosa a mencionar aquí es que en C ++ 11 Standard es posible dar valores de plantilla predeterminados para funciones usando template. En pre C ++ 11, los valores predeterminados para argumentos que no son de tipo parecen funcionar solo para argumentos de plantilla de clase. Por lo tanto, en C ++ 11, no sería necesario tener definiciones de función duplicadas como se indicó anteriormente. En C ++ 11 también es válido tener argumentos de plantilla const char * pero no es posible usarlos con literales como __FILE__o __func__como se menciona aquí .

Entonces, al final, si está usando C ++ o C ++ 11, esta podría ser una alternativa muy interesante que usar macro para obtener la línea de llamada.


1

Uso __LINE__, pero ¿cuál es su tipo?

LÍNEA El supuesto número de línea (dentro del archivo fuente actual) de la línea fuente actual (una constante entera).

Como una constante entera , el código a menudo puede asumir que el valor es __LINE__ <= INT_MAXy, por tanto, el tipo int.

Para imprimir en C, printf()necesita el especificador de juego: "%d". Esta es una preocupación mucho menor en C ++ con cout.

Preocupación pedante: si el número de línea excede INT_MAX1 (algo concebible con 16 bits int), es de esperar que el compilador produzca una advertencia. Ejemplo:

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

Alternativamente, el código podría obligar a tipos más amplios a evitar tales advertencias.

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 las limitaciones de enteros: stringify . El código podría imprimirse directamente sin una printf()llamada: algo bueno para evitar en el manejo de errores 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 Ciertamente una mala práctica de programación para tener un archivo tan grande, sin embargo, quizás el código generado por la máquina puede ser alto.

2 En la depuración, a veces el código simplemente no funciona como se esperaba. Llamar a funciones complejas como *printf()puede generar problemas en sí mismo frente a un simple fputs().


1

Para aquellos que puedan necesitarlo, una macro "FILE_LINE" para imprimir fácilmente archivos y líneas:

#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.