I'm trying to create a function that can take multiple parameters of the same type, passed in as a template. The number of arguments is known in compile time:
struct Foo
{
int a, b, c;
};
template <uint32_t argsCount, typename T>
void fun(T ...args) // max number of args == argsCount
{
// ...
// std::array<T, argsCount>{ args... };
}
int main()
{
fun<3, Foo>( { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } );
// Dont want to do:
// fun( Foo{}, Foo{}, Foo{} );
// nor:
// fun<Foo, Foo, Foo>( ... );
return 0;
}
I must take into consideration these constraints:
- no heap memory allocations
- no va_args
Is it possible to do something similar in C 14 (preferably C 14, but curious what are the solutions in newer versions)?
edit: cleaned up the initial sloppy pseudcode.
CodePudding user response:
With a slightly different syntax on the call site, you could do it by taking a reference to an array:
template<typename T, std::size_t N>
void fun(const T(& args)[N]) {
auto args_as_stdarray = std::to_array(args);
}
fun<Foo>({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
CodePudding user response:
template <class T0, class...Ts,
std::enable_if_t< std::is_same_v<T0, Ts> && ..., bool > =true
>
void fun(T0 arg0, Ts ...args)
{
constexpr auto argsCount = sizeof...(args) 1;
std::array<T0, argsCount>{ arg0, args... };
}
this is c 17; it could be done in c 14, it would just require more typing and I am lazy (replacing the fold expression mainly).
This requires the call site have Foo
s, which you don't want.
template<std::size_t I, class T>
struct helper { using type = T; };
template<std::size_t I, class T>
using X = typename helper<I,T>::type;
template<class T, std::size_t...Is>
void fun(std::index_sequence<Is...>, X<Is, T>... ts) {
std::array<T, sizeof...(Is)> args(ts...);
}
use:
fun<Foo>( std::make_index_sequence<7>, /* 7 Foo objects */ );
we can also do:
fun<Foo, 7>()( /* 7 Foo objects */ );
with a slight change:
template<class T, std::size_t N>
auto fun() {
auto seq = std::make_index_sequence<N>{};
return []<std::size_t...Is>(std::index_sequence<Is...>) {
return [](X<Is, Foo>... ts) {
std::array<Foo, sizeof...(Is)> args{ts...};
};
}( seq );
}
this is c 20, but you can backport to c 14 by writing argument pack unpackers (the boilerplate is, however, quite long and complicated).
The trick here is that foo<Foo,7>()
returns a function object that knows it takes 7
Foo
objects exactly.
CodePudding user response:
If you change the function into a functor, you can introduce a parameter pack in the body of the type of the functor.
First create a helper to turn <N, T>
-> std::tuple<T, T, T, ..., T>
(N
times):
template<std::size_t N, typename T>
struct repeat {
private:
// enable_if<true, T> == type_identity<T>
template<std::size_t... I>
static std::enable_if<true, std::tuple<std::enable_if_t<I==I, T>...>>
get(std::index_sequence<I...>);
public:
using type = typename decltype(get(std::make_index_sequence<N>{}))::type;
};
Then have your functor take a std::tuple<Args...>
, where Args
will be a parameter pack with T
N
times:
template<typename T, typename Args>
struct fun_t;
template<typename T, typename... Args>
struct fun_t<T, std::tuple<Args...>> {
static constexpr std::size_t argsCount = sizeof...(Args);
void operator()(Args... args) const {
// ...
// std::array<T, argsCount>{ args... };
}
};
template<std::uint32_t argCount, typename T>
constexpr fun_t<T, typename repeat<argCount, T>::type> fun;
int main() {
fun<3, Foo>({ 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 });
}