2009-07-10 10 views
5

Consideremos el siguiente código:C++ identificador de llamada de función

void Foo() { 
    ...... 
    LOG_ERROR("I'm error 1") // call 1 
    ..... 
    LOG_ERROR("I'm error 2") // call 2 
    ..... 

} 

LOG_ERROR() es una macro. LOG_ERROR() debe imprimir una cadena que lo identifique en el código, mientras que la suposición es que el código puede cambiar, pero A::Foo() se mantendrá sin cambios. El identificador debe retener mientras cambia el código .

Esto se puede resolver mediante la adición de código de error como argumento para LOG_ERROR(), pero queremos eliminar del programador de la carga de gestionar el error códigos.

Usar __LINE__ no es una respuesta, ya que Foo() puede pasar de la compilación a la compilación .

Por lo tanto pensé en la identificación de LOG_ERROR() relativo al inicio de Foo():

  • a. Identifique por nombre de archivo (__FILE__) + nombre de función (__FUNCTION__) + número de línea de LOG_ERROR() relativo a Foo() inicio.
  • b. Identificar por nombre de archivo (__FILE__) + nombre de función (__FUNCTION__) + LOG_ERROR() número de llamada en Foo().

La solución debería funcionar con VC++ 2008 y g ++ 4.1.1 al menos.

Una solución propuesta (link text) es:

#define ENABLE_LOG_ERROR static const int LOG_ERROR_start_line = __LINE__ 
#define LOG_ERROR(s) cerr << "error #" << (__LINE__ - LOG_ERROR_start_line) \ 
    << " in " << __func__ << ": " << s << endl 

void Foo() { 
    ENABLE_LOG_ERROR; 
    //... 
    LOG_ERROR("error 1"); 
    int i; 
    LOG_ERROR("error 2"); 
} 

Esto obligará a los usuarios a escribir en ENABLE_LOG_ERROR inicio de cada función que contiene LOG_ERROR() y hay muchas de esas funciones.

¿Hay alguna otra forma de realizar la tarea?

+0

@idimba, si no es demasiado problema, puede usar una secuencia de comandos para agregar automáticamente 'ENABLE_LOG_ERROR;' justo después de cada nombre de función. Utilizo ese truco cuando exploro * código de terceros. –

+0

Buena idea, pero influirá en __LINE__ que utilizamos extensamente en nuestro subsistema de registro. – dimba

Respuesta

0

Mediante la modificación de la idea de pila, utilice un mapeo std::map desde std::string hasta el recuento y busque el nombre de la función.

std::map<std::string, int> LOG_ERROR_count_map; 
#define LOG_ERROR(s) {\ 
    int count = ++LOG_ERROR_count_map[ __func__ ];\ 
    std::cout << count << " in " __func__ ": " s << std::endl;\ 
} 

Esto significa que no es necesario el ENABLE_LOG_ERROR, pero a costa de un mapa de consulta para cada registro. (Es un compromiso entre la facilidad de uso y el tiempo.)

+0

Tanto mi solución como GMan no dan la enésima instancia de LOG_ERROR en la función, sino la enésima llamada de LOG_ERROR en la función. Por lo tanto, no pueden usarse para simplemente contar los usos (que probablemente sea el uso previsto) cuando se produce la bifurcación. Cualquier forma de método de recuento dinámico no funcionará. –

+0

Probablemente sea obvio, pero vale la pena señalar que no puede usar el preprocesador para recuentos estáticos basados ​​en el nombre de la función porque no hay manera de detectar cuándo la función ha cambiado: las cadenas no se pueden comparar. –

1

Esta solución es no estándar, pero ambos MSVC y apoyo GCC __COUNTER__, que se incrementa cada vez que se invoca.

#define LOG_ERROR(s) cerr << "error #" << (__COUNTER__) << " in " \ 
<< __func__ << ": " << s << endl 

Tenga en cuenta que __COUNTER__ se restablecerá en cada unidad de compilación, y sólo en cada unidad de compilación. Por lo tanto, si Foo() tiene 7 LOG_ERROR() macros, en una función posterior Bar() el valor de __COUNTER__ será 7 para el primer uso de LOG_ERROR().

+0

Sin embargo, esto no funcionará entre funciones. – GManNickG

+0

El problema real es que si inserta un nuevo mensaje, los que están debajo se vuelven a numerar. –

+0

@GMan: tiene razón en que no se reiniciará para cada función. He editado para aclarar eso. @Earwicker: También es correcto. Esta es una solución bastante frágil. Supone que 'LOG_ERROR()' s solo se agregan después de todas las invocaciones anteriores en la misma unidad de compilación. – Geerad

1

Si bien la pregunta es sobre formas de generar identificadores de línea únicos dentro de una función para fines de registro, voy a dar un paso atrás y ver el problema real a resolver: Cómo generar resultados de registro que pueden identificar fácilmente la línea de código fuente sin poner la carga en el escritor del código.

Supongamos que está integrando una versión de compilación única en cada versión de su programa (que es una buena idea en general). Supongamos también que está utilizando un mecanismo de control de código fuente que mantiene el historial de su código fuente (que también es una muy buena idea para hacer de todos modos) y que puede presentarle la fuente tal como estaba para cualquier versión de compilación solicitada del programa.

Si esas suposiciones son ciertas, entonces una solución es hacer que su programa escriba su versión actual en el archivo de registro. Luego, cada entrada de registro individual puede simplemente registrar el número de línea a través del __LINE__.

Por lo tanto, cuando alguien necesita usar el registro: pueden ver el número de versión en el registro, tomar la fuente correspondiente del repositorio de control de código fuente y usar los números de línea del registro para ir a la fuente correcta líneas. Esto supone un poco más carga para la persona que utiliza la salida de registro. Sin embargo, si el código registrado depende o está influenciado por otro código que podría cambiar de una versión a otra, entonces el estado histórico del código fuente podría ser necesario de todos modos.

Además, una ventaja de trabajar de esta manera es que elimina la necesidad de suponer que una función dada permanecerá sin cambios, como originalmente era parte de la pregunta. Entonces este método tiene una aplicación mucho más amplia.


En cuanto a la aplicación va, usted podría conectarse ya sea la versión del programa cuando el programa se pone en marcha o se puede hacer el registro de macro incluirlo en cada entrada.

Si la versión del programa normalmente se almacena en un lugar no accesible en el código fuente normal, entonces podría crear un paso previo a la construcción que extraería la versión y la escribiría en un archivo simple version.h como #define o const cuerda. Entonces, el código de registro o macro podría usarlo automáticamente para generar siempre la versión actual del programa.

Cuestiones relacionadas