Use macro to generate empty code for C++ class methods

Viewed 112

Let's say I have a class like below:

#define LOGGER_CLASS_ENABLED
    
#ifdef LOGGER_CLASS_ENABLED
        
class Logger{
public:
    void println(const String&);
};
    
Logger this_is_a_singleton_logger; 
    
#define MY_LOGGER this_is_a_singleton_logger
    
#endif

Throughout my code, whenever I want to log something, I use the following shortcut:

MY_LOGGER.println("Hello") 

Now, lets say I want to disable the Logger class. In this scenario, I don't want any logging code to be generated. So, the usual 'C-style' approach is this:

#ifdef LOGGER_CLASS_ENABLED  
MY_LOGGER.println("Hello");
#endif

and repeat it for every call.

However, since there are hundreds of such calls, surrounding each print function with an extra two lines everywhere will make the code hard to read. What should I do so that I don't add two lines everywhere, keeping the only a single line of MY_LOGGER.println("Hello"), but make the macro replace it with empty line. Is it possible?

I tried following but it gave me an error:

#ifndef LOGGER_CLASS_ENABLED
    
#define MY_LOGGER.println(x) // replace it with emtpiness
    
#endif
4 Answers

Make a macro that's conditionally no-op, like standard assert:

#ifdef LOGGER_CLASS_ENABLED
#define LOG(...) MY_LOGGER.println(__VA_ARGS__) 
#else
#define LOG(...) ((void)0)
#endif

// ...
LOG("Hello");

Whereas having a macro "LOG"

#if defined(LOGGER_CLASS_ENABLED)
#define LOG(msg) MY_LOGGER.println(msg) 
#else
#define LOG(msg) do {} while(false)
#endif

would allow to entirely disable logging (and so msg is not evaluated, so LOG argument should not have side effect).

To keep usage MY_LOGGER.println(x) and does "nothing" (it is still evaluate x though, with its side effects) You might create a null object:

#ifdef LOGGER_CLASS_ENABLED
    
class RealLogger
{
public:
    void println(const String& msg) { /*Real logging*/}
};

using Logger = RealLogger;

#else // !LOGGER_CLASS_ENABLED

class NullLogger
{
public:
    void println(const String&) { /*empty*/ }
};

using Logger = NullLogger;

#endif

Logger this_is_a_singleton_logger; 
#define MY_LOGGER this_is_a_singleton_logger

As others have said, you can't have a macro name containing a special character, such as the dot operator.

However, depending on the complexity of your Logger class, you may be better off just conditionally defining 'real' and 'dummy' members of that class. Doing this may actually simplify your design; further, for the 'dummy' compilation, a decent compiler would likely optimize away all references to the Logger class, as it will be able to see that nothing is ever actually done by it.

Here's a possible (minimalistic) solution:

#include <iostream>
#include <string>
using String = std::string; // As you've used the term "String" in your code!

#define LOGGER_CLASS_ENABLED

class Logger {
public:
    #ifdef LOGGER_CLASS_ENABLED
    void println(const String& str) { std::cout << str << std::endl; }
    // ... and similar definitions for any other 'real' class functions
    #else
    void println(const String&) {}
    // ... and similar placeholder definitions for the dummy class
    #endif
};

Logger MY_LOGGER; // Note that we no longer need a conditional macro for this!

int main()
{
    std::cout << "Hello!" << std::endl;
    MY_LOGGER.println("Logging...");
    std::cout << "Goodbye!" << std::endl;
    return 0;
}

Macrodefinition is a replaceable token -- identifier. MY_LOGGER.println(x) is a sequence of tokens and cannot be a name of macrodefinition. You have to use a functional macro, e.g. variadic one:

#define LOG_PRINT(...)  MY_LOGGER.println(__VA_ARGS__)

__VA_ARGS__ is a built-in macro which would be replaced by arguments given in stead of ellipsis. At this point in C++ you might as well use variadic template functions if feature is available.

A more problematic case is when your logger uses chaining operator<< (in STL style). In that case you should have an alternative inline-able definition of logger class which does nothing and where all << will become no-ops.

Related