Esiste un modo per scrivere il seguente come C ++ macro?
-
25-09-2019 - |
Domanda
my_macro << 1 << "hello world" << blah->getValue() << std::endl;
dovrebbe espandersi in:
std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());
Soluzione
#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 a che il lavoro temporaneo come farebbero su un ostringstream
.
Quando l'istruzione estremità (es. Subito dopo il punto e virgola in tutta l'operazione di stampa in main ()), l'oggetto temporaneo va fuori del campo di applicazione e viene distrutto. Le chiamate my_stream
destructor ThreadSafeLogging
con i dati "raccolti" in precedenza.
Tested (g ++).
Grazie / crediti a dingo per aver ricordato come semplificare il tutto, quindi non ho bisogno del operator<<
sovraccarico. Peccato upvotes non possono essere condivise.
Altri suggerimenti
Non potevi solo derivare da ostream e fornire il proprio thread-safe implementazione? Poi si può solo fare
myCOutObject << 1 << "hello world" << blah->getValue() << std::endl;
E ottenere la stessa identica funzionalità senza macro e l'utilizzo di C ++ correttamente?
No. Il problema è che senza utilizzando la sintassi funzione, una macro è limitato al solo sostituito dove sia.
Ma se erano disposti ad utilizzare la sintassi funzione, è quindi possibile sostituire roba sia prima che dopo le args.
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());
Date un'occhiata al 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.
In considerazione di avere queste linee inclusi da qualche parte nel codice, sì è possibile
#include <iostream>
#include <sstream>
macro __LINE__
è definito da tutti i compilatori standart.
Così possiamo utilizzare per generare un nome di variabile, che è diversa ogni volta che si utilizza la macro:)
Ecco una nuova versione che è visto come solo un uno-dichiarazione: (Montaggio)
#define Var_(Name, Index) Name##Index
#define Var(Name, Index) Var_(Name, Index)
#define my_macro \
for (struct { int x; std::ostringstream oss; } Var(s, __LINE__) = { 0 }; \
Var(s, __LINE__).x<2; ++Var(s, __LINE__).x) \
if (Var(s, __LINE__).x==1) ThreadSafeLogging(Var(s, __LINE__).oss.str()); \
else Var(s, __LINE__).oss
// So you can use it like this
int main()
{
if (4 != 2)
my_macro << 4 << " hello " << std::endl;
my_macro << 2 << " world !" << std::endl;
}
Developper probabilmente non è necessario utilizzare questa macro due volte sulla stessa linea lo stavano ristrutturando di semplicità dell'operatore <<
. Ma nel caso in cui avete bisogno di questo, è possibile cambiare l'uso di __LINE__
da __COUNTER__
(che non è di serie!). Grazie a Quuxplusone per questo suggerimento
Ecco un altro brutto scherzo che ho visto da qualche altra parte. Ha un notevole svantaggio rispetto al mio altra risposta: non è possibile utilizzare due volte nello stesso ambito, perché dichiara una variabile. Tuttavia, può essere ancora interessante per altro i casi in cui si desidera avere run somemacro foo
qualcosa di dopo foo
.
#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;
}
Il setup 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 del file e numero di linea (s) o livelli di priorità. La funzione ShouldLog può cambiare tra le invocazioni, così si potrebbe strozzare o uscita limite. L'uscita log utilizza due funzioni di modificarsi, prefisso che aggiunge un "file: riga: (PRIO)" il prefisso alla linea, e filo () che entrambi espelle verso l'uscita log come unico comando e aggiunge una nuova riga ad essa . Nella mia applicazione lo fa sempre, ma si può fare che condizionale se uno non è già presente.