2010-03-31 12 views
7

Tengo una clase de C++ que quiero inspeccionar. Entonces, me gustaría que todos los métodos impriman sus parámetros y el retorno, justo antes de salir.Ejecutando cierto código para cada llamada de método en C++

Lo último parece algo fácil. Si hago volver() para todo, una macro

#define return(a) cout << (a) << endl; return (a) 

lo haría (que puede estar mal) si padronize todas las devoluciones a entre paréntesis (o lo que se puede llamar). Si quiero sacar esto, solo comenta el definir.

Sin embargo, la impresión de entradas parece más difícil. ¿Hay alguna manera de hacerlo, usando estructuras C++ o con un hack workaroud?

+4

¿Hay algún motivo por el que no pueda usar un depurador? – tloach

+0

Sí, deseo enviar entradas y salidas a un archivo para trabajar con los datos. No quiero depurar el método, aparentemente es correcto y está bien probado. –

Respuesta

4

Sin embargo, las entradas de impresión parece más difícil. ¿Hay alguna manera de hacerlo, usando estructuras C++ o con un hack de workaroud ?


Actualización: voy a perder algo de concisión en mi respuesta por lo que sugiere que es probable que pueda lograr lo que necesita mediante la aplicación de Design by Contract.

+2

Corto, dulce, al grano, correcto. +1. –

2

Parece que usted desea utilizar una utilidad de depuración para mí. Eso le permitirá ver todos los parámetros que desee.

+0

No quiero ver los parámetros. Quiero mostrarlos todos para hacer algo diferente, como un estudio estadístico, etc., que no es parte de la utilidad. –

1

Si sus métodos son todos virtual, puede usar el decorator-pattern para lograr eso de una manera muy elegante.

EDIT: De su comentario anterior (desea la salida para las estadísticas) concluyo que definitivamente debe utilizar el decorador-patrón. Está destinado para este tipo de cosas.

+0

Subclases lograr lo mismo en esta misma situación, ¿no? Sin embargo, eso no es lo que estoy buscando. Tanto la decoración como la creación de subclases requerirían la misma cantidad de código para escribir que el truco sucio (colocar copias en todas partes). Sin embargo, también debería cambiar el código de la persona que llama. –

+0

Bueno, dice en su perfil que le gusta el buen diseño ... Lamentablemente, la implementación de patrones de diseño en C++ siempre viene con una gran cantidad de placa de caldera, pero todavía tiene el beneficio de una gran flexibilidad. –

+0

Sí, realmente me gustan los patrones de diseño, GoF es el único libro que se sienta en mi mesa todo el tiempo. Pero creo que para este caso es excesivo. Quiero una capacidad temporal de "activar/desactivar" para el propósito de los desarrolladores. –

8

Unas pocas opciones vienen a la mente:

  • Utilice un depurador.
  • Usa decorator pattern, como Space_C0wb0y sugirió. Sin embargo, esto podría ser una gran cantidad de tipeo manual, ya que tendría que duplicar todos los métodos en la clase decorada y agregar el registro usted mismo. Tal vez usted podría automatizar la creación del objeto decorador ejecutando Doxygen en su clase y luego analizar su salida ...
  • Uso aspect-oriented programming. (Registro, que es lo que está queriendo hacer, es una aplicación común de AOP.) Wikipedia lists unas pocas implementaciones de AOP para C++: AspectC++, XWeaver y FeatureC++.
+0

+1 para la punta de AOP! Sin embargo, lea las inquietudes que le hice a quien me sugiera usar un depurador y a Space_C0wb0y –

+0

Decorar o crear subclases haría que cambie el código de la persona que llama. Incluso siendo capaz de hacerlo, no quiero hacerlo. Es mejor realizar los cambios in situ con define (#ifdef STATS ... #endif) que tener que realizar una llamada de decoración para cada objeto de esta clase creado. –

2

Si no te importa la inserción de un código a mano, se puede crear una clase que:

  1. registros de entrada para el método en el constructor
  2. proporciona un método para volcar parámetros arbitrarios
  3. proporciona un método para registrar el estado de
  4. registros de salida con estado grabado en el destructor

El uso sería algo como:

unsigned long long 
factorial(unsigned long long n) { 
    Inspector inspect("factorial", __FILE__, __LINE__); 
    inspect.parameter("n", n); 
    if (n < 2) { 
     return inspect.result(1); 
    } 
    return inspect.result(n * fact(n-1)); 
} 

Por supuesto, se puede escribir macros para declarar el inspector y controlar los parámetros. Si está trabajando con un compilador que soporta variables macros lista de argumentos, entonces se puede obtener el resultado para parecerse a:

unsigned long long 
factorial(unsigned long long n) { 
    INJECT_INSPECTOR(n); 
    if (n < 2) { 
     return INSPECT_RETURN(1); 
    } 
    return INSPECT_RETURN(n * fact(n-1)); 
} 

No estoy seguro de si se puede obtener una solución más limpia sin tener que ir a algo así como un AOP entorno o alguna herramienta de generación de código personalizado.

+0

Bueno, aunque los varargs probablemente tengan problemas. C++ no funciona bien con las funciones varargs, que eventualmente necesitará usar. Son compatibles, pero en realidad solo para valores de C. –

+0

Estaba pensando en la lista de argumentos variables _macros_ para 'INJECT_INSPECTOR' aunque todavía no se han grabado oficialmente en C++. Las funciones de Varargs en C++ definitivamente se rompen y se usan de manera real. Utilizaría una llamada 'parámetro (nombre, valor)' separada para cada parámetro, convertiría la llamada en una plantilla y aprovecharía iostreams para el formateo.Estoy usando algo similar en la práctica hoy;) –

0

Solo utilizaría una biblioteca de registro (o algunas macros) e inserta las llamadas de registro manual. A menos que su clase tenga una cantidad desmesurada de métodos, probablemente sea más rápido ponerse en marcha que desarrollar y depurar una solución más sofisticada.

+0

El registro podría hacer muy bien lo que está buscando, será un poco más limpio que imprimir cada línea, pero tendrá una biblioteca adicional dando vueltas (a menos que ya tenga una). –

Cuestiones relacionadas