Home > Net >  Is there any way to construct an std::initializer_list from an unknown number of arguments?
Is there any way to construct an std::initializer_list from an unknown number of arguments?

Time:09-01

Hi there so im having some trouble battling a silly API. We have a function that is something along the lines of

void foo(std::initializer_list<T> access_list);

And what I want to do is take a run-time JSON array and use it to call this function. for example suppose the JSON was

data : [
  {
    x : 10,
    y : 20
  },
  {
    x : 30,
    y : 40
  },
  ...
]

Then id want to call foo with foo({10,20,30,40}).

The problem is, the JSON array can be any length so id have to construct the list in a loop (i.e. into a vector) and then call foo on the constructed list. This is not possible as std::initializer_list does not have any functions to modify it after its initialisation and has no way of converting from a container (such as vector/array etc.) to an std::initializer_list.

I understand this is a misuse of std::initializer_list but is there any way (macros welcome) to create such a list?

I think one approach might be to convert the std::vector into a parameter pack and then a macro on the parameter pack to form the std::initializer_list but im not exactly sure how that would look. Thanks :).

CodePudding user response:

If you are stuck with void foo(std::initializer_list<T> access_list); and you've populated a vector<T> with the data you'd like to supply to foo<T>, then you could build a runtime translator.

Caveats:

  • The max number of elements you aim to support must be known at compile time.
  • It's terribly slow to compile.
  • It instantiates a function template 1½ times the number of elements in the initializer_list<T> you aim to support.
  • It uses a binary search to find the correct overload in run-time. This is pretty quick though.

If you aim to support relatively few elements (I use 512 in my example) you may be able to live with this.

namespace detail {
template<class Func, class T, size_t... Is>
decltype(auto) helper(Func&& f, const std::vector<T>& vec,
                      std::index_sequence<Is...>)
{
    if constexpr(sizeof...(Is) > 512) { // will throw a runtime exception
        throw std::runtime_error("more than 512 elements not supported");
    } else {
        if(sizeof...(Is)   255 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   256>{});
        if(sizeof...(Is)   127 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   128>{});
        if(sizeof...(Is)   63 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   64>{});
        if(sizeof...(Is)   31 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   32>{});
        if(sizeof...(Is)   15 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   16>{});
        if(sizeof...(Is)   7 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   8>{});
        if(sizeof...(Is)   3 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   4>{});
        if(sizeof...(Is)   1 < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   2>{});
        if(sizeof...(Is) < vec.size())
            return helper(std::forward<Func>(f), vec,
                   std::make_index_sequence<sizeof...(Is)   1>{});

        // time to do the actual call:
        return f({vec[Is]...});
    }
}
} // namespace detail

template<class Func, class T>
decltype(auto) call(Func&& f, const std::vector<T>& vec) {
    return detail::helper(std::forward<Func>(f), vec,
                          std::make_index_sequence<0>{});
}

You'd then call foo<T> like so:

std::vector<T> vec = ...;
call(foo<T>, vec);

Demo - likely to time out during compilation - but works if you don't have such a limit.

  •  Tags:  
  • c
  • Related