为 fmt 构建命名参数的动态列表

Posted

技术标签:

【中文标题】为 fmt 构建命名参数的动态列表【英文标题】:building a dynamic list of named arguments for fmt为 fmt 构建命名参数的动态列表 【发布时间】:2016-09-14 14:51:19 【问题描述】:

我正在使用fmt library,需要建立一个动态的参数列表。基本上,我有一个类似于文档页面的格式字符串

fmt::print("Hello, name", fmt::arg("name", "test"), ...);

但是参数列表(包括有多少个参数)只有在运行时才知道。我查看了fmt::ArgList,它包含fmt::Arg 实例的列表。但是命名参数是一个内部类fmt::internal::NamedArg,我看不到如何传递给列表。

有什么想法吗?

【问题讨论】:

如果不深入研究代码,这似乎是不可能的。使用字符串流还是the fmt libraries write API?或者如果您要打印的数据是相同的,那么也许使用this issue regarding std::initializer_list 中描述的东西,它应该很容易替换为例如一个std::vector。使用问题中提出的解决方案,您可以使用例如std::any(或boost::any)的向量。如果您没有要打印的所有项目的统一类型。 【参考方案1】:

我找到了一个使用 fmtlib 内部的解决方案。以下代码使用 fmtlib 格式化字符串字典中的字符串。必须包含特殊处理来处理 arg-counts >= 16,因为 fmtlib 对较小的参数列表使用了优化。

// helper only:
inline void set_type(fmt::ULongLong& result, uint32_t index, fmt::internal::Arg::Type t)

    unsigned shift = index * 4;
    uint64_t mask = 0xf;
    result |= static_cast<uint64_t>(t) << shift;


// input: 
//     pattern = fmt::format string
//     vars = dictionary of string/string arguments
// output:
//     formatted string
std::string dformat(const std::string& pattern, const std::unordered_map<std::string, std::string>& vars)

    // this is a vector of "named arguments" - straightforward enough.
    std::vector<fmt::internal::NamedArg<char>> av;

    // fmtlib uses an optimization that stores the types of the first 16 arguments as 
    // bitmask-encoded 64-bit integer. 
    fmt::ULongLong types = 0;

    // we need to build the named-arguments vector. 
    // we cannot resize it to the required size (even though we know it - we have the
    // dictionary), because NamedArg has no default constructor.
    uint32_t index = 0;
    for (const auto& item : vars)
    
        av.emplace_back(fmt::internal::NamedArg<char>(item.first, item.second));

        // we need to pack the first 16 arguments - see above
        if (index < fmt::ArgList::MAX_PACKED_ARGS)
        
            set_type(types, index, fmt::internal::Arg::NAMED_ARG);
        
        ++index;
    

    // and this is a bit tricky: depending on the number of arguments we use two mutually
    // incompatible vectors to create an arglist. It has everything to do with the speed
    // (and memory) optimization above, even though the code looks nearly identical.
    if (index >= fmt::ArgList::MAX_PACKED_ARGS)
    
        std::vector<fmt::internal::Arg> avdata;

        // note the additional terminating Arg::NONE
        avdata.resize(vars.size() + 1);
        index = 0;
        for (const auto& item : av)
        
            avdata[index].type = fmt::internal::Arg::NAMED_ARG;
            avdata[index].pointer = &av[index];
            ++index;
        
        return fmt::format(pattern, fmt::ArgList(types, &avdata[0]));
    
    else
    
        std::vector<fmt::internal::Value> avdata;

        // no need for terminating Arg::NONE, because ARG_NONE is the last encoded type 
        avdata.resize(vars.size());
        index = 0;
        for (const auto& item : av)
        
            avdata[index].pointer = &av[index];
            ++index;
        
        return fmt::format(pattern, fmt::ArgList(types, &avdata[0]));
    

示例用法:

std::unordered_map<std::string, std::string> vars;
vars["FIRSTNAME"] = "Foo";
vars["LASTNAME"] = "Bar";

std::string result = dformat("Hello FIRSTNAME LASTNAME, how are you doing", vars);

【讨论】:

【参考方案2】:

fmt 从 7.0.0 开始内置支持动态参数列表。

命名的动态参数:

fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("const", "pi"));
store.push_back(fmt::arg("val", 3.14f));

std::string result = fmt::vformat("const = val", store);
// result is "pi = 3.14"

未命名的动态参数:

fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back("answer to everything");
store.push_back(42);

std::string result = fmt::vformat(" is ", store);
// result is "answer to everything is 42"

【讨论】:

以上是关于为 fmt 构建命名参数的动态列表的主要内容,如果未能解决你的问题,请参考以下文章

颤振运行->错误:没有名称为“keyboardDismissBehavior”的命名参数

混合命名和未命名函数参数

函数的动态参数,命名空间

python之动态参数 *args,**kwargs和命名空间

函数的动态参数与命名空间 day9

Laravel:如何通过javascript加载带有动态参数的命名路由