C++14 solution that implements apply
from C++17 half way down.
template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};
template<class=void,std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&&f)->decltype(auto) {
return decltype(f)(f)( index<Is>... );
};
}
template<std::size_t N>
auto index_over( index_t<N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
template<class F>
auto for_each_arg( F&& f ) {
return [f = std::forward<F>(f)](auto&&...args)->decltype(auto) {
using discard=int[];
(void)discard{0,(void(
f(decltype(args)(args))
),0)...};
};
}
These are useful, but not needed:
template<class F, class Tuple>
decltype(auto) apply( F&& f, Tuple&& tuple ) {
auto count = index< std::tuple_size< std::decay_t<Tuple> >{} >;
return index_over( count )( [&](auto...Is) {
using std::get;
return std::forward<F>(f)( get<decltype(Is)::value>( std::forward<Tuple>(tuple) )... );
} );
}
template<class Tuple>
auto for_each_tuple_element(Tuple&& tuple) {
return [&](auto&& f)->decltype(auto){
return apply(
for_each_arg( decltype(f)(f) ),
std::forward<Tuple>(tuple)
);
};
}
test code:
int main() {
std::tuple< std::vector<int>, std::vector<char> > tup;
for_each_tuple_element( tup )( [](auto&& v) {
v.push_back(3);
});
std::cout << std::get<0>(tup).size() << "," << std::get<1>(tup).size() << "\n";
}
live example
Then we can apply it to your problem.
ComponentHandle create(Entity e, Fields ...fields) {
m_entity.push_back(e);
auto indexer = index_over<sizeof...(fields>();
auto fields_tuple = std::forward_as_tuple( std::forward<Fields>(fields)... );
indexer( for_each_arg([&](auto Is){
std::get<Is>(m_fields).push_back(
std::get<Is>(decltype(fields_tuple)(fields_tuple))
);
} );
}
index_over
takes a compile-time N and returns a lambda. This lambda takes a callable, and calls it with index_t<0>
through index_t<N-1>
.
for_each_arg
takes a callable, and returns a lambda that takes any number of arguments. It calls the callable with each of those arguments in turn.
Stitched together we take our Fields...fields
, build an index_over
it to get us a compile-time set of indexes into it. We then store those Fields in a tuple
of r and l value references.
We write an operation on a single index Is
. We then pass that to for_each_arg
, and pass the return value to the index_over
, and get the single index-handling lambda to be called for each index.
Some compilers don't permit a non-constexpr
std::integral_constant
to convert to a scalar in a constexpr
context. They are wrong and/or obsolete. For those, you'll have to do decltype(Is)::value
.