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

devrait se développer dans:Est-il possible d'écrire ce qui suit en tant que macro C++?

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

Je me demande si vous pouvez faire #define my_macro (bla) {std :: ostringstream oss; bla d'Oss; ThreadSafeLogging (oss.str()); } –

+0

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

Répondre

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

temporaire de type my_stream est créé, qui est une sous-classe de ostringstream. Toutes les opérations à ce travail temporaire comme ils le feraient sur un ostringstream.

Lorsque l'instruction se termine (c'est-à-dire juste après le point-virgule sur l'ensemble de l'opération d'impression dans main()), l'objet temporaire est hors de portée et est détruit. Le destructeur my_stream appelle ThreadSafeLogging avec les données "collectées" précédemment.

Testé (g ++).

Merci/crédits à dingo pour souligner comment simplifier le tout, donc je n'ai pas besoin de la operator<< surchargé. Trop mauvais upvotes ne peuvent pas être partagés.

+7

C'est l'utilisation (ou l'abus) la plus brillante des destructeurs de C++ que j'ai vus dans ma vie. – anon

+0

Non, la macro crée un temporaire, qui est détruit après l'exécution de la ligne contenant l'exécution temporaire. –

+0

Je reprends ma réserve de destructeur; cela détruit immédiatement en raison de la température créée. Bien fait. – MikeSep

2

Non. Le problème est que sans utiliser la syntaxe de fonction, une macro est limitée à seulement être remplacé où il est. Mais si vous vouliez utiliser la syntaxe de la fonction, vous pouvez ensuite remplacer les éléments avant et après les arguments.

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

Vous pourriez en définissant MaMacro comme:

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

Compte tenu de la réponse de Nicolás, il semble que « Non » est incorrect. –

3

Vous ne pouviez pas simplement dériver d'ostream et fournir votre propre implémentation thread-safe? Ensuite, vous pouvez simplement faire

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

Et obtenir exactement la même fonctionnalité sans macros et en C++ correctement?

2

Jetez un oeil à google-glog, ils le font à l'aide d'un objet temporaire instancié avec un

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

et ils ont aussi d'autres macros intéressantes telles que LOG_IF et al.

1

Voici un autre mauvais tour que j'ai vu ailleurs. Il a un désavantage important par rapport à mon autre réponse: vous ne pouvez pas l'utiliser deux fois dans la même portée car il déclare une variable. Cependant, il peut toujours être intéressant pour autres cas où vous voulez avoir somemacro foo exécuter quelque chose aprèsfoo.

#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

Bien sûr, vous pouvez l'utiliser plus d'une fois :)! La macro __LINE__ est définie par tous les compilateurs standard. On peut donc l'utiliser pour générer le nom variable ostrinstream :)

#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 pas deux fois sur la même ligne, mais p .. qui ferait ça !!?

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

Pourquoi lui donner un nom? Vous pouvez utiliser un objet 'MyObject()' comme un objet temporaire et vous attendre à ce qu'il soit détruit à la fin de l'instruction. – dascandy

+0

Clever! Notez que sur tous les compilateurs populaires, vous pouvez utiliser le '__COUNTER__' non standard au lieu du' __LINE__' standard et cela fonctionnera même plusieurs fois sur la même ligne. Cependant, dans les deux cas, cette macro n'est pas hygiénique; si vous dites 'if (log) my_macro << 4 << std :: endl;' alors vous passerez un mauvais moment. La réponse la mieux votée, basée sur l'astuce du destructeur LOG() de google-glog, est hygiénique. – Quuxplusone

2

La configuration de l'exploitation forestière j'est assez similaire:

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 votre enregistrement est désactivé, le ostream est jamais créé et peu existe au-dessus. Vous pouvez configurer la consignation sur le nom de fichier & numéro (s) de ligne ou niveaux de priorité. La fonction ShouldLog peut changer entre les invocations, vous pouvez donc limiter ou limiter la sortie. La sortie du journal utilise deux fonctions pour se modifier, Préfixe qui ajoute un préfixe "file: line: (PRIO)" à la ligne, et Flush() qui le renvoie à la sortie du journal en une seule commande et y ajoute un retour à la ligne . Dans ma mise en œuvre, c'est toujours le cas, mais vous pouvez rendre cela conditionnel si on n'en est pas déjà là.