Search code examples
c++c++20variadic

How to pass variadic function parameters to another function in C++ (specifically std::format)?


I'm trying to write a logger that can take arguments the same way that printf does with formatting supported. I'm planning to just call std::format on the input to my logger, but I need to forward the variadic args to std::format. How can I do that?

What I want it too look like:

void log(const std::string& msg)
{
  std::cout << msg << std::endl; // I'm actually wrapping another logger here, this is just an example.
}

void log(const std::string& fmt_string ...);
{
  std::string msg = std::format(gmt_string, ...);
  log(msg);
}

Solution

  • You don't want the C-style ... parameter. You want a variadic template:

    template <typename T, typename ...P>
    void log(T &&format, P &&... params)
    {
        std::string msg = fmt::format(std::forward<T>(format), std::forward<P>(params)...);
        std::cout << msg << '\n';
    }
    

    Notice forwarding references for the parameter, pack instead of const references. Const references had caused problems for me before (spdlog used to use const references, and fmt::join didn't like them).

    Notice the first parameter being templated. At least with libfmt, this is necessary to be able to receive FMT_STRING(...) parameters. With std::format, an std::string_view would probably suffice. const std::string & is uncool because it forces a heap allocation.

    Also I'm not sure how I feel about having a separate parameter-less overload. It means that you have to escape { and } only when there's at least one parameter, which is not nice.

    Also consider using spdlog, which is very similar. It wraps libfmt, and can direct output to various/arbirary targets.