2010-02-03 16 views
20
my_macro << 1 << "hello world" << blah->getValue() << std::endl; 

debería ampliar en:¿Hay alguna forma de escribir lo siguiente como una macro de C++?

std::ostringstream oss; 
oss << 1 << "hello world" << blah->getValue() << std::endl; 
ThreadSafeLogging(oss.str()); 
+0

Me pregunto si puedes hacer #define my_macro (blah) {std :: ostringstream oss; oss blah; ThreadSafeLogging (oss.str()); } –

+0

Ver también: https://stackoverflow.com/questions/4446484/a-line-based-thread-safe-stdcerr-for-c –

Respuesta

72
#define my_macro my_stream() 
class my_stream: public std::ostringstream { 
public: 
    my_stream() {} 
    ~my_stream() { 
     ThreadSafeLogging(this->str()); 
    } 
}; 
int main() { 
    my_macro << 1 << "hello world" << std::endl; 
} 

Un temporal del tipo my_stream se crea, que es una subclase de ostringstream. Todas las operaciones para ese trabajo temporal como lo harían en un ostringstream.

Cuando la instrucción finaliza (es decir, justo después del punto y coma de toda la operación de impresión en main()), el objeto temporal sale del alcance y se destruye. El destructor my_stream llama al ThreadSafeLogging con los datos "recopilados" anteriormente.

Probado (g ++).

Gracias/créditos a dingo para señalar cómo simplificar todo, así que no necesito el sobrecargado operator<<. Las votaciones negativas demasiado malas no se pueden compartir.

+7

Este es el uso (o abuso) más brillante de los destructores de C++ que he visto en mi vida. – anon

+0

No, la macro crea un temporal, que se destruye después de que se ejecuta la línea que contiene el temporal. –

+0

Recupero mi advertencia de destructor; esto se destruye inmediatamente debido a la temperatura que se está creando. Bien hecho. – MikeSep

2

No. El problema es que sin el uso de sintaxis de la función, un macro se limita a solamente siendo sustituido donde está.

Pero si estaba dispuesto a usar la sintaxis de la función, puede reemplazar cosas tanto antes como después de los argumentos.

my_macro(1 << "hello world" << blah->getValue() << std::endl); 

Usted podría definiendo MiMacro como:

#define my_macro(args) std::ostreamstring oss; \ 
         oss << args; \ 
         ThreadSafeLogging(oss.str()); 
+5

Dada la respuesta de Nicolás, parece que "no" Es incorrecto. –

3

¿No podría derivar de ostream y proporcionar su propia implementación segura de subprocesos? Entonces podría simplemente hacer

myCOutObject << 1 << "hello world" << blah->getValue() << std::endl; 

¿Y obtener la misma funcionalidad exacta sin macros y utilizando C++ correctamente?

2

Tome un vistazo a google-glog, lo hacen utilizando un objeto temporal instanciado con un

LOG(INFO) << "log whatever" << 1; 

y también tienen otras macros interesantes como LOG_IF et al.

1

Aquí hay otro truco desagradable que vi en otro lado. Tiene una desventaja significativa en comparación con mi otra respuesta: no puede usarla dos veces en el mismo ámbito porque declara una variable. Sin embargo, todavía puede ser interesante para otros casos donde desea tener somemacro foo ejecutar algo después defoo.

#define my_macro \ 
    std::ostringstream oss; \ 
    for (int x=0; x<2; ++x) \ 
     if (x==1) ThreadSafeLogging(oss.str()); \ 
     else oss 

int main() { 
    my_macro << 1 << "hello world" << std::endl; 
} 
2

¡Por supuesto que puede usarlo más de una vez :)! __LINE__ macro está definida por todos los compiladores estándar. lo tanto, podemos utilizarlo para generar ostrinstream nombre de la variable :)

#define Var_(Name, Index) Name##Index 
#define Var(Name, Index) Var_(Name, Index) 
#define my_macro \ 
    std::ostringstream Var(oss, __LINE__);   \ 
for (int x=0; x<2; ++x) \ 
    if (x==1) std::cout << Var(oss, __LINE__).str(); \ 
    else Var(oss, __LINE__) 

Ok ok no dos veces en la misma línea; p .. pero ¿quién haría eso !!?

int main() { 
    my_macro << 4 << " hello " << std::endl; 
    my_macro << 2 << " world !" << std::endl; 
} 
+0

¿Por qué darle un nombre? Puede usar un objeto 'MyObject()' como temporal y confiar en que se destruirá al final de la instrucción. – dascandy

+0

¡listo! Tenga en cuenta que en todos los compiladores populares puede usar el '__COUNTER__' no estándar en lugar del estándar '__LINE__' y luego funcionará incluso varias veces en la misma línea. Sin embargo, en cualquier caso, esta macro no es higiénica; si dices 'if (log) my_macro << 4 << std :: endl;' entonces pasarás un mal momento. La respuesta más votados, basada en el truco destructor LOG() de google-glog, es higiénica. – Quuxplusone

2

La configuración de registro que tengo es bastante similar:

bool ShouldLog(const char* file, size_t line, Priority prio); 

class LoggerOutput : public std::stringstream { 
public: 
    LoggerOutput(const char* file, size_t line, Priority prio) 
    : prio(prio) 
    { 
    Prefix(file, line, prio); 
    } 
    void Prefix(const char* file, size_t line, Priority prio); 
    ~LoggerOutput() { 
    Flush(); 
    } 
    void Flush(); 
private: 
    Priority prio; 
}; 

#define LOG(Prio) if (!Logging::ShouldLog(__FILE__, __LINE__, Prio)) {} else Logging::LoggerOutput(__FILE__, __LINE__, Prio) 

Si su registro está deshabilitado, el ostream Nunca se crea y existe poca sobrecarga. Puede configurar el registro en el nombre de archivo & número (s) de línea o niveles de prioridad. La función ShouldLog puede cambiar entre invocaciones, por lo que puede acelerar o limitar la salida. La salida de registro utiliza dos funciones para modificarse, Prefijo que agrega un prefijo "file: line: (PRIO)" a la línea y Flush() que lo vacía al resultado de registro como un solo comando y le agrega una nueva línea. . En mi implementación, siempre lo hace, pero puede hacer eso condicional si uno no está ya allí.

Cuestiones relacionadas