为现有宏的新版本提供参数

Providing new version of existing macro with parameters

我们的代码中有一个现有的日志宏。让我们称之为 LOG_XXX,其中 XXX 是要使用的日志级别,例如 LOG_INFO、LOG_DEBUG、LOG_ERROR 等。我们将日志消息流式传输到宏:

LOG_INFO << "This is a log message.";

在幕后,宏创建了一个公开流的临时对象。当对象被销毁时,使用流内容调用记录器。

随着我们代码库的增长,我们希望添加具有键值对的结构化日志记录。我们还想在宏中添加一个强制性的 "module name",强制调用者指定软件中的哪个逻辑模块正在执行日志记录。

LOG_INFO("some module) << make_pair("RequestId", "foo") << make_pair("Msg", "This is a log message.");

我可以通过创建一个不同的对象来实现它,该对象的构造函数采用模块名称,并重载 << 运算符以采用字符串对。

现在我可以将这个新宏命名为 LOG_INFO2 并完成它。但我觉得这个名字很丑。这在概念上实际上是一个重载,但宏不能被重载。

一种方法是实现一个选择器,根据参数的数量选择正确的宏。但由于原始调用站点没有括号 LOG_INFO(),我认为这行不通。我之前问过

我可以让调用者直接实例化对象,但他们不必知道实现所有这些的对象是什么:

LogWriter(module, LogLevel.Info) << make_pair("Msg", "This is a log message");

我的总体目标是不必编辑原始调用并添加新功能。除了我想到的,还有其他选择吗?我做错了宏选择选项吗?

您可以将宏更改为 return 带有 operator ()(const std::string&) 的对象,例如:

struct Logger2
{
    explicit Logger2(const std::string& name) : name(name) {}
    Logger2& operator << (const std::pair<const char*, const char*>& p)
    {
        std::cout << name << ":" << p.first << " " << p.second << std::endl;
        return *this;
    }
    std::string name;
};

struct Logger
{
    Logger& operator << (const std::string& s)
    {
        std::cout << "Log:" << s << std::endl;
        return *this;
    }


    Logger2 operator () (const std::string& name) const
    {
        return Logger2{name};
    }
};


#define LOG Logger{}

Live Demo