libfmt中是否有非取巧方式在运行时构造命名参数名称?
解决libfmt动态命名参数的更优方案
核心问题拆解
用libfmt构建代码生成器时,需要在运行时构造fmt::dynamic_format_arg_store的命名参数,但fmt::arg要求传入const char*,直接用运行时生成的std::string无法编译。当前通过符号表存储参数名再取c_str()的方式可行,但想寻求更优雅的实现。
可行的优化方案
1. 规范生命周期管理(最直接的合理方案)
libfmt的设计要求命名参数的名称字符串在格式化过程中保持有效,你之前的符号表思路并非hack,只是可以更规整:
- 用一个容器(比如
std::vector<std::string>或std::unordered_map<std::string, std::any>)统一存储所有动态生成的参数名,确保容器生命周期覆盖从创建args到调用fmt::vformat的整个过程。 - 直接通过
c_str()获取参数名指针添加到参数存储中,示例代码:
#include <fmt/format.h> #include <fmt/args.h> #include <string> #include <vector> int main() { std::string format_str = "int {var_out} = function_call({var_in}, {dep_param__var_out});"; fmt::dynamic_format_arg_store<fmt::format_context> args; // 统一存储动态参数名,确保生命周期足够 std::vector<std::string> dynamic_param_names; dynamic_param_names.emplace_back("dep_param__var_out"); int dep_value = 42; // 添加动态参数 args.add(fmt::arg(dynamic_param_names.back().c_str(), dep_value)); // 添加静态参数 args.add(fmt::arg("var_out", "result")); args.add(fmt::arg("var_in", "input_val")); std::string result = fmt::vformat(format_str, args); fmt::print("{}", result); return 0; }
2. 封装辅助函数简化代码
如果觉得每次写c_str()麻烦,可以封装一个辅助函数自动处理转换,同时明确生命周期依赖:
#include <fmt/format.h> #include <fmt/args.h> #include <string> template <typename T> auto make_dynamic_arg(const std::string& name, T&& value) { return fmt::arg(name.c_str(), std::forward<T>(value)); } int main() { std::string format_str = "int {var_out} = function_call({var_in}, {dep_param__var_out});"; fmt::dynamic_format_arg_store<fmt::format_context> args; std::string dep_name = "dep_param__var_out"; int dep_value = 42; args.add(make_dynamic_arg(dep_name, dep_value)); args.add(fmt::arg("var_out", "result")); args.add(fmt::arg("var_in", "input_val")); std::string result = fmt::vformat(format_str, args); fmt::print("{}", result); return 0; }
3. 自定义参数存储结构
如果需要更灵活的参数管理,可以直接构建包含参数名和对应fmt::basic_format_arg的映射,一次性添加到动态参数存储:
#include <fmt/format.h> #include <fmt/args.h> #include <string> #include <unordered_map> int main() { std::string format_str = "int {var_out} = function_call({var_in}, {dep_param__var_out});"; fmt::dynamic_format_arg_store<fmt::format_context> args; std::unordered_map<std::string, fmt::basic_format_arg<fmt::format_context>> dynamic_params; std::string dep_name = "dep_param__var_out"; dynamic_params[dep_name] = fmt::arg(dep_name.c_str(), 42); // 将映射中的参数添加到存储 for (const auto& [name, arg] : dynamic_params) { args.add(arg); } args.add(fmt::arg("var_out", "result")); args.add(fmt::arg("var_in", "input_val")); std::string result = fmt::vformat(format_str, args); fmt::print("{}", result); return 0; }
总结
libfmt的设计决定了动态命名参数需要用户自行管理参数名字符串的生命周期,这并非缺陷而是合理的设计权衡。你之前的符号表思路是正确的,通过规范容器存储、封装辅助函数等方式可以让代码更整洁优雅,而非hack。
内容的提问来源于stack exchange,提问作者Wintermute
相关产品推荐
相关产品推荐

