特化带参数的宏
Specializing macro with arguments
假设我有以下宏:
#define LOG(L, ...) func(L, __VA_ARGS__);
其中 L 可以是 INFO, WARN, FATAL
之一
现在我想以不同的方式为 FATAL
定义它。
#define LOG(FATAL, ...) {func(FATAL, __VA_ARGS__); exit(-1);}
如何实现?
编辑:
作为上述的跟进,有没有更好的方法呢?例如,通过避免使用宏。
宏在 C++ 中大多是一个糟糕的选择——主要是因为它们与命名空间无关,并且可能会在意想不到的地方生效。
就是说 – 关于如何解决 OP 问题的示例,例如使用 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. :-(");
}
输出:
INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became worst. :-(
Program aborted!
后续问题的另一个示例 - 使用可变模板而不是宏:
#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");
}
输出:
INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became bad. :-(
FATAL: Things became worst. XXX
Program aborted!
我必须承认我需要一些帮助来解包参数 – 在这里找到:
SO: What is the easiest way to print a variadic parameter pack using std::ostream?.
稍微扩展 Scheffs 的答案,这个将日志级别作为模板参数传递,允许使用 if constexpr
(正如 JVApen 在他的评论中提到的)。您需要在编译时知道每个日志输出的相应级别,但我认为这不会成为问题。
enum Level { Info, Warn, Error, Fatal };
template<Level L, typename ...ARGS>
void log(ARGS&& ... args)
{
static const char *levelText[] = { "INFO", "WARNING", "ERROR", "FATAL" };
std::cerr << levelText[L] << ": ";
(std::cerr << ... << args);
std::cerr << '\n';
if constexpr (L == Fatal)
std::cerr << "Program aborted\n";
}
假设我有以下宏:
#define LOG(L, ...) func(L, __VA_ARGS__);
其中 L 可以是 INFO, WARN, FATAL
现在我想以不同的方式为 FATAL
定义它。
#define LOG(FATAL, ...) {func(FATAL, __VA_ARGS__); exit(-1);}
如何实现?
编辑: 作为上述的跟进,有没有更好的方法呢?例如,通过避免使用宏。
宏在 C++ 中大多是一个糟糕的选择——主要是因为它们与命名空间无关,并且可能会在意想不到的地方生效。
就是说 – 关于如何解决 OP 问题的示例,例如使用 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. :-(");
}
输出:
INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became worst. :-(
Program aborted!
后续问题的另一个示例 - 使用可变模板而不是宏:
#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");
}
输出:
INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became bad. :-(
FATAL: Things became worst. XXX
Program aborted!
我必须承认我需要一些帮助来解包参数 – 在这里找到:
SO: What is the easiest way to print a variadic parameter pack using std::ostream?.
稍微扩展 Scheffs 的答案,这个将日志级别作为模板参数传递,允许使用 if constexpr
(正如 JVApen 在他的评论中提到的)。您需要在编译时知道每个日志输出的相应级别,但我认为这不会成为问题。
enum Level { Info, Warn, Error, Fatal };
template<Level L, typename ...ARGS>
void log(ARGS&& ... args)
{
static const char *levelText[] = { "INFO", "WARNING", "ERROR", "FATAL" };
std::cerr << levelText[L] << ": ";
(std::cerr << ... << args);
std::cerr << '\n';
if constexpr (L == Fatal)
std::cerr << "Program aborted\n";
}