使用宏为 C++ class 方法生成空代码

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

假设我有一个 class 如下所示:

#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

在我的整个代码中,每当我想记录一些东西时,我都会使用以下快捷方式:

MY_LOGGER.println("Hello") 

现在,假设我想禁用 Logger class。在这种情况下,我不希望生成任何日志记录代码。所以,通常的 'C-style' 方法是这样的:

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

并在每次通话时重复。

但是,由于有数百个这样的调用,因此在每个打印函数周围多加两行代码会使代码难以阅读。我应该怎么做才能不在任何地方添加两行,只保留一行 MY_LOGGER.println("Hello"),而是让宏用空行替换它。可能吗?

我试过跟随,但它给了我一个错误:

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

制作一个有条件的无操作宏,如标准 assert:

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

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

宏定义是一个可替换的标记 -- 标识符。 MY_LOGGER.println(x) 是一个标记序列,不能是宏定义的名称。您必须使用功能宏,例如可变参数之一:

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

__VA_ARGS__ 是一个内置宏,它将被代替省略号的参数替换。此时在 C++ 中,如果功能可用,您也可以使用可变参数模板函数。

更有问题的情况是当您的记录器使用链接运算符<<(STL 样式)时。在那种情况下,您应该有一个替代的记录器 class 的可内联定义,它什么都不做,并且所有 << 都将变为无操作。

正如其他人所说,您的宏名称不能包含特殊字符,例如点运算符。

但是,根据您的 Logger class 的复杂性,您最好只是有条件地定义 class 的 'real' 和 'dummy' 成员].这样做实际上可以简化您的设计;此外,对于 'dummy' 编译,一个体面的编译器可能会优化所有对 Logger class 的引用,因为它将能够看到它实际上什么也没做。

这是一个可能的(简约的)解决方案:

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

而有一个宏“LOG

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

将允许完全禁用日志记录(因此 msg 未被评估,因此 LOG 参数不应有副作用)。

为了保持使用 MY_LOGGER.println(x) 并且“什么都不做”(尽管它仍然在评估 x,但有其副作用) 您可以创建 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