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);
}
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.