Suppose I have a printf
-like function (used for logging) utilizing perfect forwarding:
template<typename... Arguments>
void awesome_printf(std::string const& fmt, Arguments&&... args)
{
boost::format f(fmt);
f % /* How to specify `args` here? */;
BlackBoxLogFunction(boost::str(f).c_str());
}
(I didn't compile this but my real function follows this guideline)
How can I "unroll" the variadic argument into the boost::format variable f
?
As is usual with variadic templates, you can use recursion:
std::string awesome_printf_helper(boost::format& f){
return boost::str(f);
}
template<class T, class... Args>
std::string awesome_printf_helper(boost::format& f, T&& t, Args&&... args){
return awesome_printf_helper(f % std::forward<T>(t), std::forward<Args>(args)...);
}
template<typename... Arguments>
void awesome_printf(std::string const& fmt, Arguments&&... args)
{
boost::format f(fmt);
auto result = awesome_printf_helper(f, std::forward<Arguments>(args)...);
// call BlackBoxLogFunction with result as appropriate, e.g.
std::cout << result;
}
Demo.
In C++17, simply (f % ... % std::forward<Arguments>(args));
will do.
I did some googling and found an interesting solution:
#include <iostream>
#include <boost/format.hpp>
template<typename... Arguments>
void format_vargs(std::string const& fmt, Arguments&&... args)
{
boost::format f(fmt);
int unroll[] {0, (f % std::forward<Arguments>(args), 0)...};
static_cast<void>(unroll);
std::cout << boost::str(f);
}
int main()
{
format_vargs("%s %d %d", "Test", 1, 2);
}
I don't know if this is a recommended solution but it seems to work. I don't like the hacky static_cast
usage, which seems necessary to silence the unused variable warnings on GCC.
Just to summarize the void.pointer's solution and the hints proposed by Praetorian, T.C. and Jarod42, let me provide the final version (online demo)
#include <boost/format.hpp>
#include <iostream>
template<typename... Arguments>
std::string FormatArgs(const std::string& fmt, const Arguments&... args)
{
boost::format f(fmt);
std::initializer_list<char> {(static_cast<void>(
f % args
), char{}) ...};
return boost::str(f);
}
int main()
{
std::cout << FormatArgs("no args\n"); // "no args"
std::cout << FormatArgs("%s; %s; %s;\n", 123, 4.3, "foo"); // 123; 4.3; foo;
std::cout << FormatArgs("%2% %1% %2%\n", 1, 12); // 12 1 12
}
Also, as it was noted by T.C., using the fold expression syntax, available since C++17, the FormatArgs function can be rewritten in the more succinct way
template<typename... Arguments>
std::string FormatArgs(const std::string& fmt, const Arguments&... args)
{
return boost::str((boost::format(fmt) % ... % args));
}