Search code examples
c++variadic-macros

Specializing macro with arguments


Suppose I have the following macro:

#define LOG(L, ...) func(L, __VA_ARGS__);

Where L can be one of INFO, WARN, FATAL

Now I want to define it for FATAL differently.

#define LOG(FATAL, ...) {func(FATAL, __VA_ARGS__); exit(-1);}

How to accomplish this?

Edit: As a follow up to above, is there a better way to do it? i.e. by avoiding macros for example.


Solution

  • Macros are mostly a bad choice in C++ – essentially because of they are namespace agnostic and may take effect where it is unexpected.

    That said – a sample for how OPs issue could be solved e.g. using token pasting:

    #include <iostream>
    
    #define LOG(LEVEL, ...) LOG_##LEVEL(__VA_ARGS__)
    
    #define LOG_INFO(...) log(Info, __VA_ARGS__)
    #define LOG_WARN(...) log(Warn, __VA_ARGS__)
    #define LOG_FATAL(...) do { log(Error, __VA_ARGS__); std::cerr << "Program aborted!\n"; } while (false)
    
    enum Level { Info, Warn, Error };
    
    void log(Level level, const char *text)
    {
      static const char *levelText[] = { "INFO", "WARNING", "ERROR" };
      std::cerr << levelText[level] << ": " << text << '\n';
    }
    
    int main()
    {
      LOG(INFO, "Everything fine. :-)");
      LOG(WARN, "Not so fine anymore. :-|");
      LOG(FATAL, "Things became worst. :-(");
    }
    

    Output:

    INFO: Everything fine. :-)
    WARNING: Not so fine anymore. :-|
    ERROR: Things became worst. :-(
    Program aborted!
    

    Live Demo on coliru


    Another sample for the follow-up question - with variadic templates instead of macros:

    #include <iostream>
    
    enum Level { Info, Warn, Error, Fatal };
    
    template <typename ...ARGS>
    void log(Level level, ARGS&& ... args)
    {
      static const char *levelText[] = { "INFO", "WARNING", "ERROR", "FATAL" };
      std::cerr << levelText[level] << ": ";
      (std::cerr << ... << args);
      std::cerr << '\n';
      if (level == Fatal) std::cerr << "Program aborted!";
    }
    
    int main()
    {
      log(Info, "Everything fine.", ' ', ":-)");
      log(Warn, "Not so fine anymore.", ' ', ":-|");
      log(Error, "Things became bad.", ' ', ":-(");
      log(Fatal, "Things became worst.", ' ', "XXX");
    }
    

    Output:

    INFO: Everything fine. :-)
    WARNING: Not so fine anymore. :-|
    ERROR: Things became bad. :-(
    FATAL: Things became worst. XXX
    Program aborted!
    

    Live Demo on coliru

    I must admit that I needed some help for the parameter unpacking – found here:
    SO: What is the easiest way to print a variadic parameter pack using std::ostream?.