使用宏为 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
假设我有一个 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