I am attempting to make a std::format
callback lambda:
auto callback = lazy_format("Hello, my name is {}, my age is {}, my hobby is {}", "Arthur", 8, "Dinosaurs");
...
if (ask_about_themselves) {
std::string response = callback();
}
And I'm running into trouble trying to figure out how to get the forwarding to work correctly with my one largest constraint:
I can't call std::vformat
(forbidden for our team except in special cases, and I want other teammates to be able to write these without too much worrying).
My attempts have included all kinds of combinations of forwarding and mucking around with templates, with this initial design as my base:
template<typename... Args>
inline auto lazy_format(std::format_string<Args...> fmt, Args&&... args)
{
return [fmt, ...args = std::forward<Args>(args)]() {
std::format(fmt, std::forward<Args>(args)...);
};
}
The big issues come from slight type differences between the std::format_string
I capture and how the arguments are captured.
For example, I'm passing in a const char(&)[7]
as my first argument, which gets stored in format_string
's type, but that array reference info gets lost when capturing it in the lambda. So, I wind up trying to pass a const char* const
into a const char(&)[7]
parameter.
The closest I've come is with help from Antyer's solution, but it still seems to have a problem with copying const
references instead of passing the reference to the lambda.
I want to preserve references where possible: https://godbolt.org/z/G38jMaEba
template<typename... Args>
inline auto lazy_format(std::format_string<const std::decay_t<Args>&...> fmt, Args&&... args)
{
return [fmt=std::move(fmt), ...args=std::forward<Args>(args)]() {
return std::format(fmt, args...);
};
}
You explicitly store lvalue references or values via
std::tuple<Args...>
and forward into original value categories when expanding its elements usingstd::apply
Demo