Search code examples
c++fmt

Is there a non-hacky way in libfmt to construct names for named arguments at runtime?


I am using libfmt to build a code generator that generates a sort of adapter layer around an existing library. So I have a dataset of parameter descriptions that include format strings describing the conversion from the data type in the outer layer to the data type in the inner layer. In the most simple case, this might look like

"{type_out} {var_out} = {var_in};\n"

in a more complex case, the conversion might depend on another parameter:

"int {var_out} = function_call({var_in}, {dependent_param__var_out});\n"

The name dependent_param (and as such any name that refers to one of its attributes, such as dependent_param__var_out) is not known to the code generator at compile time; it's constructed at runtime from the parameter dataset.

This means that I need to build a fmt::dynamic_format_arg_store some of whose named arguments are constructed at runtime. In essence, what I would like is along the lines of

#include <fmt/args.h>
#include <fmt/format.h>
#include <string>

fmt::dynamic_format_arg_store<fmt::format_context>
construct_arg_store(std::string const &paramname) {
  fmt::dynamic_format_arg_store<fmt::format_context> fmt_args;

  // so far, so good.
  fmt_args.push_back(fmt::arg("var_in", paramname));
  fmt_args.push_back(fmt::arg("var_out", paramname + "_out"));

  // imagine this were constructed by iterating over runtime-available data.
  std::string argname  = "dependent_param__var_out";
  std::string argvalue = "dependent_param_out";

  // Does not compile; fmt::arg expects char const *
  fmt_args.push_back(fmt::arg(argname, argvalue));

  return fmt_args;
}

int main() {
  std::string fmtstring = "int {var_out} = function_call({var_in}, {dependent_param__var_out});\n";

  auto args = construct_arg_store("foo");

  fmt::vprint(fmtstring, args);
}

Right now, I build a symbol table from the dataset that contains std::string objects for all possible format arg names and use their .c_str() to build the fmt::arg objects (so that the generated C strings have a long enough lifetime). This works, but it seems like a bit of a dirty hack.

So I'm wondering, is there a better way to do it than that?


Solution

  • In general, it's better to use a template system like Mustache for this. That said, storing argument names as std::strings on the side and use them to construct dynamic_format_arg_store is OK. There is nothing hacky about it.