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.