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

dovrebbe ampliare in:Esiste comunque la possibilità di scrivere quanto segue come macro C++?

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

Mi chiedo se si può fare #define my_macro (blah) {std :: ostringstream oss; oss blah; ThreadSafeLogging (oss.str()); } –

+0

Vedi anche: https://stackoverflow.com/questions/4446484/a-line-based-thread-safe-stdcerr-for-c –

risposta

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

Una temporanea di tipo my_stream è stato creato, che è una sottoclasse di ostringstream. Tutte le operazioni su quel lavoro temporaneo come farebbero su un ostringstream.

Quando l'istruzione termina (cioè subito dopo il punto e virgola sull'intera operazione di stampa in main()), l'oggetto temporaneo esce dall'ambito e viene distrutto. Il distruttore my_stream chiama ThreadSafeLogging con i dati "raccolti" in precedenza.

Testato (g ++).

Grazie/crediti a dingo per indicare come semplificare il tutto, quindi non ho bisogno del sovraccarico operator<<. Gli upvotes troppo negativi non possono essere condivisi.

+7

Questo è l'uso (o abuso) più brillante dei distruttori C++ che ho visto nella mia vita. – anon

+0

No, la macro crea un temporaneo, che viene distrutto dopo la riga contenente gli esecuzioni temporanee. –

+0

riprendo il mio avvertimento al distruttore; questo si distrugge immediatamente a causa della creazione della temperatura. Ben fatto. – MikeSep

2

No. Il problema è che senza usare la sintassi funzione, una macro è limitato ad essere sostituita solo dove è.

Ma se si desidera utilizzare la sintassi della funzione, è possibile sostituire le cose prima e dopo gli argomenti.

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

Si potrebbe definendo MyMacro come:

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

Data la risposta di Nicolás, sembra che "No." non è corretto. –

3

Non potresti semplicemente derivare da ostream e fornire la tua implementazione thread-safe? Quindi puoi semplicemente fare

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

E ottenere esattamente la stessa funzionalità senza macro e utilizzando C++ correttamente?

2

Date un'occhiata a google-glog, lo fanno utilizzando un oggetto temporaneo istanziare con un

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

e hanno anche altre macro interessanti, come LOG_IF et al.

1

Ecco un altro brutto trucco che ho visto da qualche altra parte. Ha uno svantaggio significativo rispetto alla mia altra risposta: non è possibile utilizzarlo due volte nello stesso ambito perché dichiara una variabile. Tuttavia, potrebbe essere ancora interessante per altri casi in cui si desidera avere somemacro foo eseguire qualcosa dopofoo.

#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

Naturalmente è possibile utilizzarlo più di una volta :)! __LINE__ la macro è definita da tutti i compilatori standard. Così possiamo usare per generare ostrinstream nome della variabile :)

#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 non due volte sulla stessa linea; p .. ma che avrebbe fatto quello !!?

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

Perché dargli un nome? È possibile utilizzare un oggetto 'MyObject()' come temporaneo e affidarsi alla sua distruzione alla fine dell'istruzione. – dascandy

+0

Intelligente! Si noti che su tutti i compilatori popolari è possibile utilizzare il '__COUNTER__' non standard invece dello standard' __LINE__' e quindi funzionerà anche più volte sulla stessa riga. Tuttavia, in entrambi i casi, questa macro non è igienica; se dici 'if (log) my_macro << 4 << std :: endl;' allora avrai un brutto momento. La risposta più votata, basata sul trucco distruttore LOG() di google-glog, è igienica. – Quuxplusone

2

La messa a punto di registrazione che ho è abbastanza simile:

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) 

Se la registrazione è disattivata, l'ostream viene mai creato ed esiste poco overhead. È possibile configurare la registrazione sul nome file & numero/i di riga o livelli di priorità. La funzione ShouldLog può cambiare tra le invocazioni, in modo da poter limitare o limitare l'output. L'output del registro utilizza due funzioni per modificare se stesso, Prefisso che aggiunge un prefisso "file: line: (PRIO)" alla riga e Flush() che lo svuota sull'output del log come un singolo comando e aggiunge una nuova riga ad esso . Nella mia implementazione lo fa sempre, ma puoi renderlo condizionale se non lo sei già.

Problemi correlati