Oodini
Oodini

Reputation: 1341

Convert initializer_list to variadic templates

I use a formatting library called fmt (http://fmtlib.net/latest/).

One of the possible use is :

fmt::format("Hello, {name}! The answer is {number}. Goodbye, {name}.", fmt::arg("name", "World"), fmt::arg("number", 42));

I'd like to wrap this call in a function that I'd call as :

myFunction(myString, {"name", "World"}, {"number", 42});

for any number of arguments.

Up to now,I only succeeded to do a function callable with a list of pairs :

myFunction(myString, std::make_pair("name", "World"), std::make_pair("number", 42));

with the function :

std::string myFunction(const char* str, const Args&... rest) const
{
    return fmt::format(mLocale.getPOILabel(label), fmt::arg(rest.first, rest.second)...);
}

but I'd like not to have use pairs.

What should I do ?

PS : The fmt::arg cannot be passed between functions.

Upvotes: 5

Views: 739

Answers (2)

max66
max66

Reputation: 66200

Not exactly what do you asked, because you have to call your function as

myFunction(myString, "name", "World", "number", 42);

instead of

myFunction(myString, {"name", "World"}, {"number", 42});

but the following should be an example (sorry but baz() is untested)

#include <tuple>
#include <string>
#include <utility>
#include <iostream>

// fmt headers

template <typename ... Ps>
std::string baz (char const * frmt,
                 std::pair<char const *, Ps> const & ... ps)
 { return fmt::format(frmt, fmt::arg(ps.first, ps.second)...); }

template <typename ... Ts, std::size_t ... Is>
std::string bar (char const * frmt, std::tuple<Ts...> const & t,
                 std::index_sequence<Is...> const &)
 { return baz(frmt, std::get<Is>(t)...); }

template <typename ... Ts>
std::string foo (char const * frmt, std::tuple<Ts...> const & t)
 { return bar(frmt, t, std::make_index_sequence<sizeof...(Ts)>{}); }

template <typename ... Ts, typename P1, typename P2, typename ... Args>
std::string foo (char const * frmt, std::tuple<Ts...> const & t,
                 P1 const & p1, P2 const & p2, Args const & ... args)
 { return foo(frmt, 
      std::tuple_cat(t, std::make_tuple(std::make_pair(p1, p2))), args...); }

template <typename ... Args>
std::string myFunction (char const * frmt, Args const & ... args)
 { return foo(frmt, std::tuple<>{}, args...); }

int main()
 {
   myFunction("format", "name", "World", "number", 42);
 }

Observe that this example use std::index_sequence and std::make_index_sequence(), so compile starting from C++14; but it's easy to create a substitute for this class and this function to work with C++11 too.

Upvotes: 1

Caleth
Caleth

Reputation: 62704

Try moving the results of fmt::arg, rather than copying or passing const references.

template<typename ... Args>
std::string myFunction(const char* str, Args&&... rest) const
{
    return fmt::format(mLocale.getPOILabel(label), std::forward<Args...>(rest)...);
}

myFunction(myString, fmt::arg("name", "World"), fmt::arg("number", 42));

Upvotes: 0

Related Questions