将括号括起来的初始化器列表传递给可变参数宏,并扩展为std :: pair <>

问题描述

我目前正在开发一个线程日志记录库,作为更大的图形绘图项目(用于个人学习和技能开发)的第一部分。

当前,我正在使用一个侧面运行线程的单例,并将Log消息和数据放入队列(以便它们不阻止事件),以便稍后处理。我在std :: map 周围写了一个小的包装,作为LogData,可以由记录器以以下方式在流或文件显示

[0.000155][DEBUG]: FILE LOGGER ADDED { ID="1" LVL="TRACE" }

构造函数允许传递字符串,char *,floats,int,short long等,并将其转换为字符串,以便稍后在那些括号中显示

当前构造此LogData有点肿。产生上面日志的示例:

GG::LogData id_dat;
id_dat.push("ID",id);
id_dat.push("LVL",GG::loglevel_toString(lvl));
GG_DEBUG("FILE LOGGER ADDED",id_dat);

由于我的班级是单身人士,因此我使用宏来简化操作,它们与:相同:

#define GG_TRACE(MESS,...) GG::Logging::get()->push_to_queue(GG::LOG_LEVEL::TRACE,MESS,##__VA_ARGS__);

这对于大多数用途都可以正常工作。但是我想使它可以在一行上使用,并使其不那么肿。我想要达到的效果是这样的:

//Desired Usage
GG_TRACE("VARIADIC TEST",{"X","1"},{"Y","2"},{"Z","3"});

在此处展开​​:

void Logging::push_to_queue(GG::LOG_LEVEL level,std::string mess,std::pair<const char*,std::string> log_data ...)

我将使用大括号初始化列表来生成日志数据,然后循环遍历可变参数并在函数中构造LogData,而不必每次都手动进行操作。

我可以直接使用类似这样的功能

void test(std::pair<char*,int> p) {
    GG::LogData dat;
    dat.push("key",p.first);
    dat.push("value",p.second);
    GG_TRACE("PAIR: ",dat);
}
// In main...
test({ "test",1 });

效果很好。但是,当我尝试使用相同的模式并将宏转发到push_to_queue功能时,GCC出现以下错误

GCC compile error

任何人都曾经以这种方式使用过括号括起来的初始化程序列表,或者知道如何解决错误?我对这种模式很陌生。任何其他建议或指针,以改善此表示赞赏。 (很长的帖子很抱歉)

解决方法

注意声明

void Logging::push_to_queue(GG::LOG_LEVEL level,std::string mess,std::pair<const char*,std::string> log_data ...);

没有类型std::pair<const char*,std::string>的可变数量的参数。它实际上等效于带有逗号的版本:

void Logging::push_to_queue(GG::LOG_LEVEL level,std::string> log_data,...);

,它具有一个类型为std::pair<const char*,std::string>的参数,并且是C风格的可变参数函数,仅通过<cstdarg>va_startva_arg才有第三个参数和va_end。这不是您想要的,因为这样的函数没有很好的方法来知道有多少个参数,并且括号列表永远不能是与C样式省略号匹配的参数。

获得C ++风格的可变参数函数(知道参数的数量和类型)的唯一方法是将其作为带有可变参数模板参数的模板。但是用大括号列表作为参数意味着没有模板参数推导,因此要很好地使用它会很棘手。

但是我们可以使用std::initializer_list并在宏中添加更多{}来使这种语法起作用:

#include <initializer_list>
#include <utility>
#include <string>

void GG::Logging::push_to_queue(GG::LOG_LEVEL level,std::initializer_list<std::pair<const char*,std::string>> log_data)
{
    GG::LogData dat;
    for (const auto &kv : log_data)
        dat.push(kv.first,kv.second);
    // Do the rest...
}

#define GG_TRACE(MESS,...) (GG::Logging::get()->push_to_queue( \
     GG::LOG_LEVEL::TRACE,MESS,{__VA_ARGS__}))

因此扩展将具有类似{{"X","1"},{"Y","2"},{"Z","3"}}的参数,其中外部{}用于std::initializer_list,内部{}用于每个std::pair。 / p>

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...