Splitting argpack in half? - c++

How can I split an argument pack in two equal parts?
For example, I would like to do something like this:
template<typename T> T sum(const T& t)
{ return t; }
template<typename T> T sum(const T& t1, const T& t2)
{ return t1 + t2; }
template<typename ...T> T sum(T&& ...t)
{ sum(first_half(t)...) + sum(second_half(t)...); }

I would suggest something along the lines of this, as the required nesting depth and amount of boilerplate code is lower than in the suggested solution. However, the actual parameter pack is never split, instead two ranges of indices are produced to index the input values which are forwarded as tuples to be then accessed via std::get. Aside from the nesting depth, it is far easier to specify how the splitting is performed (rounding up, down, or taking a power of two and the remainder).
int sum(int a) { return a; }
int sum(int a, int b) { return a + b; }
template<typename... Args> int sum(Args&&... args);
template<typename Tuple, size_t... index>
int sum_helper(pack_indices<index...>, Tuple&& args)
{
return sum(std::get<index>(args)...);
}
template <size_t begin, size_t end, typename... Args>
int sum_helper(Args&&... args)
{
typename make_pack_indices<end, begin>::type indices;
return sum_helper(indices, std::forward_as_tuple(std::forward<Args>(args)...));
}
template<typename... Args>
int sum(Args&&... args)
{
constexpr size_t N = sizeof...(Args);
return sum(
sum_helper<0, N/2>(std::forward<Args>(args)...),
sum_helper<N/2, N>(std::forward<Args>(args)...)
);
}
which requires
template <size_t...>
struct pack_indices {};
template <size_t Sp, typename IntPack, size_t Ep>
struct make_indices_imp;
template <size_t Sp, size_t Ep, size_t... Indices>
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep>
{
typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type;
};
template <size_t Ep, size_t... Indices>
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep>
{
typedef pack_indices<Indices...> type;
};
template <size_t Ep, size_t Sp = 0>
struct make_pack_indices
{
static_assert(Sp <= Ep, "make_tuple_indices input error");
typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type;
};

A possible solution is to convert the argument list into a tuple and then extract the needed arguments via std::get and std::index_sequence (it will appear only in C++14, but you can easily implement the same functionality in the meantime).
Untested example code follows:
template<class T1, class T2>
struct append_index_seq;
template<std::size_t N, std::size_t... NN>
struct append_index_seq<N, std::index_sequence<NN...>> {
using type = std::index_sequence<N, NN...>;
};
template<std::size_t M, std::size_t N1, std::size_t... N>
struct add_index_seq_impl {
using type = append_index_seq<N1+M, add_index_seq<N, M>::type>::type;
};
template<std::size_t M, std::size_t N1>
struct add_index_seq_impl {
using type = std::index_sequence<N1+M>::type;
};
template<std::size_t M, std::size_t... N>
struct add_index_seq;
template<std::size_t M, std::size_t... N>
struct add_index_seq<m, std::index_sequence<N...>> {
using type = add_index_seq_impl<M, N...>;
}
template<std::size_t N>
struct get_first_half {
static_assert(N % 2 == 0, "N must be even");
using indexes = std::make_index_sequence<N/2>;
};
template<std::size_t N>
struct get_second_half {
static_assert(N % 2 == 0, "N must be even");
using indexes_1st = std::make_index_sequence<N/2>;
using indexes = add_index_seq<N/2, indexes_1st>::type;
};
template<class F, class Tuple, std::size_t... I>
auto apply(F&& f, Tuple&& t, index_sequence<I...>)
{
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template<class ...T> T sum(T&& ...t)
{
auto params = std::make_tuple(t);
T r1 = apply(sum, params, get_first_half<T...>::indexes);
T r2 = apply(sum, params, get_second_half<T...>::indexes);
return r1 + r2;
}

Related

Compiler cannot handle std::invoke

What is the problem with this?
struct foo {
void process(int, char, bool) {}
};
foo myfoo;
template <typename Method> struct thing {
void doit() {
Method m = Method{};
(myfoo.*m)(5, 'a', true);
}
};
int main() {
thing<decltype(&foo::process)> t;
t.doit();
}
I think this isolates the problem. What is the workaround if I have to use the type Method, as in the case of my original post below?
Original post:
In the following attempted test:
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
As you can predict, func is supposed to carry out
foo.play('c', true); bar.jump(5, 2, 4.5); baz.run(6.8);
My implementation of the Functor class so far (ignoring perfect forwarding and such for now) is
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
where my last line using std::invoke is just to test the concept before I continue. It however will not compile on either GCC 7.2 or clang 6.0, so I cannot continue with the generalization. Any workaround here, or a completely different implementation altogether?
Here is everything I have so far:
namespace utilities {
template <std::size_t N, typename... Ts>
struct nth_element : std::tuple_element<N, std::tuple<Ts...>> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
}
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs, typename... Members> struct many_members_h;
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs>
struct many_members_h<Rs, Ts, ArgsPacks, AllArgs> {
using return_types = Rs;
using classes = Ts;
using args_packs = ArgsPacks;
using all_args = AllArgs;
};
template <typename... Rs, typename... Ts, typename... ArgsPacks, typename... AllArgs, typename R, typename T, typename... Args, typename... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Ts...>, std::tuple<ArgsPacks...>, std::tuple<AllArgs...>, R(T::*)(Args...), Rest...> :
many_members_h<std::tuple<Rs..., R>, std::tuple<Ts..., T>, std::tuple<ArgsPacks..., std::tuple<Args...>>, std::tuple<AllArgs..., Args...>, Rest...> { };
template <typename... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
// Testing
#include <iostream>
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
Taking your smaller first example, note that decltype(&foo::process) is the type called void (foo::*)(int, char, bool).
This type does not contain or imply any association with the original function foo::process itself. Just like the type int doesn't let you get the value of some particular int elsewhere in your program, or the type SomeClass doesn't let you refer to a SomeClass object elsewhere in your program, the type alone doesn't carry a value or identity.
The expression Method{} value-initializes this pointer to member type. Which means the resulting value is a null pointer value. Which means calling it is undefined behavior (and on many systems is likely to result in a segfault).
If you're using C++17 mode, you could use a template <auto Method> non-type parameter and simply pass &foo::process (without using decltype) as the template argument. Some SFINAE techniques could enforce that the argument is actually a pointer to member function, and some helper traits could be used to get the class type and parameter list tuple.
Or if you're using a standard earlier than C++17, you'll have to either make the function pointer a function argument, or make it a template parameter which follows the type, as in template <typename MethodType, MethodType Method>, then call as thing<decltype(&foo::process), &foo::process>.
Thanks to aschepler's answer and advice to use auto... instead of typename... for the member function pointers, I was able to carry the original goal:
#include <tuple>
#include <functional> // std::invoke
#include <type_traits>
#include <utility>
namespace utilities {
template <std::size_t N, auto I, auto... Is>
struct nth_element : nth_element<N - 1, Is...> { };
template <auto I, auto... Is>
struct nth_element<0, I, Is...> {
static constexpr decltype(I) value = I;
};
template <std::size_t N, typename Pack> struct nth_index;
template <std::size_t N, std::size_t... Is>
struct nth_index<N, std::index_sequence<Is...>> : nth_element<N, Is...> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
template <typename F, typename T, typename Tuple, std::size_t... Is>
decltype(auto) invoke_with_tuple_h (F&& f, T&& t, Tuple&& tuple, std::index_sequence<Is...>&&) {
return std::invoke(std::forward<F>(f), std::forward<T>(t), std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename T, typename Tuple>
decltype(auto) invoke_with_tuple (F&& f, T&& t, Tuple&& tuple) {
return invoke_with_tuple_h (std::forward<F>(f), std::forward<T>(t), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
template <typename PartialSums, std::size_t Sum, std::size_t... Is> struct all_partial_sums_h;
template <std::size_t... PartialSums, std::size_t Sum>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum> {
using type = std::index_sequence<PartialSums..., Sum>;
using type_without_last_sum = std::index_sequence<PartialSums...>; // We define this because this is what we need actually.
};
template <std::size_t... PartialSums, std::size_t Sum, std::size_t First, std::size_t... Rest>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum, First, Rest...> :
all_partial_sums_h<std::index_sequence<PartialSums..., Sum>, Sum + First, Rest...> { };
template <typename Pack> struct all_partial_sums;
template <std::size_t... Is>
struct all_partial_sums<std::index_sequence<Is...>> : all_partial_sums_h<std::index_sequence<>, 0, Is...> { };
template <typename Pack> struct pack_size;
template <template <typename...> class P, typename... Ts>
struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
template <typename PackOfPacks> struct get_pack_sizes;
template <template <typename...> class P, typename... Packs>
struct get_pack_sizes<P<Packs...>> {
using type = std::index_sequence<pack_size<Packs>::value...>;
};
}
template <typename Method> struct method_traits;
template <typename R, typename C, typename... Args>
struct method_traits<R(C::*)(Args...)> {
using return_type = R;
using class_type = C;
using args_type = std::tuple<Args...>;
};
template <typename Rs, typename Cs, typename ArgsPacks, auto... Members> struct many_members_h;
template <typename Rs, typename Cs, typename ArgsPacks>
struct many_members_h<Rs, Cs, ArgsPacks> {
using return_types = Rs;
using classes = Cs;
using args_packs = ArgsPacks;
};
template <typename... Rs, typename... Cs, typename... ArgsPacks, auto F, auto... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Cs...>, std::tuple<ArgsPacks...>, F, Rest...> :
many_members_h<std::tuple<Rs..., typename method_traits<decltype(F)>::return_type>, std::tuple<Cs..., typename method_traits<decltype(F)>::class_type>, std::tuple<ArgsPacks..., typename method_traits<decltype(F)>::args_type>, Rest...> { };
template <auto... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <auto... Members>
struct Functor {
using m = many_members<Members...>;
using starting_points = typename utilities::all_partial_sums<typename utilities::get_pack_sizes<typename m::args_packs>::type>::type;
template <typename... Args>
typename m::return_types operator()(Args&&... args) const {
constexpr std::size_t M = sizeof...(Members);
auto t = std::make_tuple(std::forward<Args>(args)...);
auto objects = utilities::tuple_head<M>(t);
auto arguments = utilities::extract_subtuple<M, sizeof...(Args) - M>(t);
return call(objects, arguments, std::make_index_sequence<M>{});
}
private:
template <typename Tuple1, typename Tuple2, std::size_t... Is>
auto call (Tuple1& objects, const Tuple2& args, std::index_sequence<Is...>&&) const { // perfect forwarding to do later
return std::make_tuple(call_helper<Is>(objects, args)...);
}
template <std::size_t N, typename Tuple1, typename Tuple2>
auto call_helper (Tuple1& objects, const Tuple2& args) const { // perfect forwarding to do later
constexpr std::size_t s = std::tuple_size_v<std::tuple_element_t<N, typename m::args_packs>>;;
constexpr std::size_t a = utilities::nth_index<N, starting_points>::value;
const auto args_tuple = utilities::extract_subtuple<a, s>(args);
return utilities::invoke_with_tuple (utilities::nth_element<N, Members...>::value, std::get<N>(objects), args_tuple);
}
};
// Testing
#include <iostream>
struct Foo { int play (char c, bool b) { std::cout << std::boolalpha << "Foo::play(" << c << ", " << b << ") called.\n"; return 3; } };
struct Bar { double jump (int a, short b, float c) { std::cout << "Bar::jump(" << a << ", " << b << ", " << c << ") called.\n"; return 5.8; } };
struct Baz { char run (double d) { std::cout << "Baz::run(" << d << ") called.\n"; return 'b'; } };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<&Foo::play, &Bar::jump, &Baz::run> func;
const auto tuple = func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
std::cin.get();
}
Output:
Baz::run(6.8) called.
Bar::jump(5, 2, 4.5) called.
Foo::play(c, true) called.

Get first N elements of parameter pack

I have to following problem:
template< size_t... N_i >
class A
{
// ...
};
template< size_t N, size_t... N_i >
A</* first N elements of N_i...*/> foo()
{
A</* first N elements of N_i...*/> a;
// ...
return a;
}
int main()
{
A<1,2> res = foo<2, 1,2,3,4>();
return 0;
}
Here, I want foo to have the return type A</* first N size_t of N_i...*/>, i.e., the class A which has as template arguments the first N elements of the parameter pack N_i.
Does anyone know how this can be implemented?
Here is the shortest solution that came to my mind (with two lines spent for an alias).
It follows a minimal, working example based on the code posted by the OP:
#include<functional>
#include<cstddef>
#include<utility>
#include<tuple>
template<std::size_t... V>
class A {};
template<std::size_t... V, std::size_t... I>
constexpr auto func(std::index_sequence<I...>) {
return A<std::get<I>(std::make_tuple(V...))...>{};
}
template<std::size_t N, std::size_t... V>
constexpr auto func() {
return func<V...>(std::make_index_sequence<N>{});
}
template<std::size_t N, std::size_t... V>
using my_a = decltype(func<N, V...>());
int main() {
A<1,2> res1 = func<2, 1, 2, 3, 4>();
// Or even better...
decltype(func<2, 1, 2, 3, 4>()) res2{};
// Or even better...
my_a<2, 1, 2, 3, 4> res3{};
}
This is a slight variation on #skypjack's answer that avoids using tuples:
template <size_t... N_i,size_t... M_i>
auto foo2(std::index_sequence<M_i...>)
{
constexpr size_t values[] = {N_i...};
return A<values[M_i]...>();
}
template <size_t N,size_t... N_i>
auto foo()
{
return foo2<N_i...>(std::make_index_sequence<N>());
}
The most direct subproblem is in the land of typelists:
template <class... Ts>
struct typelist {
using type = typelist;
static constexpr std::size_t size = sizeof...(Ts);
};
template <class T>
struct tag { using type = T; };
template <std::size_t N, class TL>
struct head_n {
using type = ???;
};
Now, head_n is just a matter of simple recursion - move an element from one list to another list N times starting from an empty list.
template <std::size_t N, class R, class TL>
struct head_n_impl;
// have at least one to pop from and need at least one more, so just
// move it over
template <std::size_t N, class... Ts, class U, class... Us>
struct head_n_impl<N, typelist<Ts...>, typelist<U, Us...>>
: head_n_impl<N-1, typelist<Ts..., U>, typelist<Us...>>
{ };
// we have two base cases for 0 because we need to be more specialized
// than the previous case regardless of if we have any elements in the list
// left or not
template <class... Ts, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<Us...>>
: tag<typelist<Ts...>>
{ };
template <class... Ts, class U, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<U, Us...>>
: tag<typelist<Ts...>>
{ };
template <std::size_t N, class TL>
using head_n = typename head_n_impl<N, typelist<>, TL>::type;
Going from this to your specific problem I leave as an exercise to the reader.
An alternate approach is via concatenation. Convert every element of a typelist<Ts...> into either a typelist<T> or a typelist<>, and then concat them all together. concat is straightforward:
template <class... Ts>
struct concat { };
template <class TL>
struct concat<TL>
: tag<TL>
{ };
template <class... As, class... Bs, class... Rest>
struct concat<typelist<As...>, typelist<Bs...>, Rest...>
: concat<typelist<As..., Bs...>, Rest...>
{ };
And then we can do:
template <std::size_t N, class TL, class = std::make_index_sequence<TL::size>>
struct head_n;
template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
: concat<
std::conditional_t<(Is < N), typelist<Ts>, typelist<>>...
>
{ };
template <std::size_t N, class TL>
using head_n_t = typename head_n<N, TL>::type;
The advantage of this latter approach is that concat can be replaced in C++17 by a fold-expression given an appropriate operator+:
template <class... As, class... Bs>
constexpr typelist<As..., Bs...> operator+(typelist<As...>, typelist<Bs...> ) {
return {};
}
which allows:
template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
{
using type = decltype(
(std::conditional_t<(Is < N), typelist<Ts>, typelist<>>{} + ... + typelist<>{})
);
};
This is fairly simple with Boost.Hana:
namespace hana = boost::hana;
template<size_t... vals>
auto make_a(hana::tuple<hana::integral_constant<size_t, vals>...>)
{
return A<vals...>{};
}
template<size_t N, size_t... vals>
auto foo(){
constexpr auto front = hana::take_front(
hana::tuple_c<size_t, vals...>,
hana::integral_c<size_t,N>
);
return detail::make_a(front);
}
live demo
You could also make use of variadic generic lambda expression and reusable helper structure to perform compile-time iteration:
#include <utility>
#include <tuple>
template <std::size_t N, class = std::make_index_sequence<N>>
struct iterate;
template <std::size_t N, std::size_t... Is>
struct iterate<N, std::index_sequence<Is...>> {
template <class Lambda>
auto operator()(Lambda lambda) {
return lambda(std::integral_constant<std::size_t, Is>{}...);
}
};
template <size_t... Is>
struct A { };
template <size_t N, size_t... Is>
auto foo() {
return iterate<N>{}([](auto... ps){
using type = std::tuple<std::integral_constant<std::size_t, Is>...>;
return A<std::tuple_element_t<ps, type>{}...>{};
});
}
int main() {
decltype(foo<3, 1, 2, 3, 4>()) a; // == A<1, 2, 3> a;
}
Unfortunately, such method requires to define additional Helper types
template< size_t... N_i >
class A
{
};
template <size_t... N_i>
struct Helper;
template <size_t... N_i>
struct Helper<0, N_i...>
{
typedef A<> type;
};
template <size_t N0, size_t... N_i>
struct Helper<1, N0, N_i...>
{
typedef A<N0> type;
};
template <size_t N0, size_t N1, size_t... N_i>
struct Helper<2, N0, N1, N_i...>
{
typedef A<N0, N1> type;
};
template< size_t N, size_t... N_i >
typename Helper<N, N_i...>::type foo()
{
typename Helper<N, N_i...>::type a;
return a;
}

Passing many functions and storing all their results in a tuple

Consider this output:
int foo (int, char) {std::cout << "foo\n"; return 0;}
double bar (bool, double, long ) {std::cout << "bar\n"; return 3.5;}
bool baz (char, short, float) {std::cout << "baz\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
multiFunction<2,3,3> (tuple, foo, bar, baz); // foo bar baz
}
So multiFunction<2,3,3> takes the first 2 elements of tuple and passes them to foo, the next 3 elements of tuple and passes them to bar, etc... I got this working (except when the functions have overloads, which is a separate problem). But the return values of each function called are lost. I want those return values stored somewhere, something like
std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo, bar, baz);
But I don't know how to implement that. For those who want to help get this done, here is my (updated) working code so far, which stores the outputs into a stringstream only. Not easy to get all the values back, especially if the objects saved in the stream are complex classes.
#include <iostream>
#include <tuple>
#include <utility>
#include <sstream>
template <std::size_t N, typename Tuple>
struct TupleHead {
static auto get (const Tuple& tuple) { // The subtuple from the first N components of tuple.
return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
}
};
template <typename Tuple>
struct TupleHead<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <std::size_t N, typename Tuple>
struct TupleTail {
static auto get (const Tuple& tuple) { // The subtuple from the last N components of tuple.
return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
}
};
template <typename Tuple>
struct TupleTail<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&) {
return f(std::get<Is>(tuple)...);
}
template <typename Tuple, typename F>
auto functionOnTuple (const Tuple& tuple, F f) {
return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}
template <typename Tuple, typename... Functions> struct MultiFunction;
template <typename Tuple, typename F, typename... Fs>
struct MultiFunction<Tuple, F, Fs...> {
template <std::size_t I, std::size_t... Is>
static inline auto execute (const Tuple& tuple, std::ostringstream& oss, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
const auto headTuple = TupleHead<I, Tuple>::get(tuple);
const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
// functionOnTuple (headTuple, f); // Always works, though return type is lost.
oss << std::boolalpha << functionOnTuple (headTuple, f) << '\n'; // What about return types that are void???
return MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, oss, std::index_sequence<Is...>{}, fs...);
}
};
template <>
struct MultiFunction<std::tuple<>> {
static auto execute (const std::tuple<>&, std::ostringstream& oss, std::index_sequence<>) { // End of recursion.
std::cout << std::boolalpha << oss.str();
// Convert 'oss' into the desired tuple? But how?
return std::tuple<int, double, bool>(); // This line is just to make the test compile.
}
};
template <std::size_t... Is, typename Tuple, typename... Fs>
auto multiFunction (const Tuple& tuple, Fs... fs) {
std::ostringstream oss;
return MultiFunction<Tuple, Fs...>::execute (tuple, oss, std::index_sequence<Is...>{}, fs...);
}
// Testing
template <typename T> int foo (int, char) {std::cout << "foo<T>\n"; return 0;}
double bar (bool, double, long ) {std::cout << "bar\n"; return 3.5;}
template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo<bool>, bar, baz<2,5,1>); // foo<T> bar baz<int...>
}
Here's an approach where the number of arguments is deduced greedily:
#include <tuple>
namespace detail {
using namespace std;
template <size_t, size_t... Is, typename Arg>
constexpr auto call(index_sequence<Is...>, Arg&&) {return tuple<>{};}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto call(index_sequence<Is...>, ArgT&&, Fs&&...);
template <size_t offset, size_t... Is,
typename ArgT, typename F, typename... Fs,
typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
constexpr auto call(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs) {
return tuple_cat(make_tuple(f(get<offset+I>(forward<ArgT>(argt))...)),
call<offset+sizeof...(Is)>(index_sequence<>{},
forward<ArgT>(argt),
forward<Fs>(fs)...));}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto call(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
return call<offset>(index_sequence<Is..., sizeof...(Is)>{},
forward<ArgT>(argt), forward<Fs>(fs)...);}
}
template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
return detail::call<0>(std::index_sequence<>{},
std::forward<ArgT>(argt), std::forward<Fs>(fs)...);}
Demo. However, the above has quadratic time complexity in the number of return values, because tuple_cat is called recursively. Instead, we can use a slightly modified version of call to obtain the indices for each call - the actual tuple is then obtained directly:
#include <tuple>
namespace detail {
using namespace std;
template <size_t, size_t... Is, typename Arg>
constexpr auto indices(index_sequence<Is...>, Arg&&) {return tuple<>{};}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto indices(index_sequence<Is...>, ArgT&&, Fs&&...);
template <size_t offset, size_t... Is, typename ArgT, typename F, class... Fs,
typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
constexpr auto indices(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs){
return tuple_cat(make_tuple(index_sequence<offset+Is...>{}),
indices<offset+sizeof...(Is)>(index_sequence<>{},
forward<ArgT>(argt),
forward<Fs>(fs)...));}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto indices(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
return indices<offset>(index_sequence<Is..., sizeof...(Is)>{},
forward<ArgT>(argt), forward<Fs>(fs)...);}
template <typename Arg, typename F, size_t... Is>
constexpr auto apply(Arg&& a, F&& f, index_sequence<Is...>) {
return f(get<Is>(a)...);}
template <typename ITuple, typename Args, size_t... Is, typename... Fs>
constexpr auto apply_all(Args&& args, index_sequence<Is...>, Fs&&... fs) {
return make_tuple(apply(forward<Args>(args), forward<Fs>(fs),
tuple_element_t<Is, ITuple>{})...);
}
}
template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
return detail::apply_all<decltype(detail::indices<0>(std::index_sequence<>{},
std::forward<ArgT>(argt),
std::forward<Fs>(fs)...))>
(std::forward<ArgT>(argt), std::index_sequence_for<Fs...>{},
std::forward<Fs>(fs)...);}
Demo 2.
Building from the ground up and ignoring perfect forwarding so that I have to type less. We need a couple helpers. First, we need a partial version of apply that takes which indices from the tuple we want to apply to the function:
<class Tuple, class F, size_t... Is>
auto partial_apply(Tuple tuple, F f, std::index_sequence<Is...>) {
return f(get<Is>(tuple)...);
}
Then, we need to call that function for each subset of the tuple. Let's say we have all of our functions and indexes wrapped in a tuple already:
template <class Tuple, class FsTuple, class IsTuple, size_t... Is>
auto multi_apply(Tuple tuple, FsTuple fs, IsTuple indexes, std::index_sequence<Is...>) {
return std::make_tuple(
partial_apply(tuple,
std::get<Is>(fs),
std::get<Is>(indexes)
)...
);
}
So in this case, we'd want to end up calling multi_apply(tuple, <foo,bar,baz>, <<0,1>,<2,3,4>,<5,6,7>>, <0, 1, 2>).
All we need know is to build the indexes part. We're starting with <2,3,3>. We need to get the partial sums (<0,2,5>) and add that to the index sequences <<0,1>,<0,1,2>,<0,1,2>>. So we can write a partial sum function:
template <size_t I>
using size_t_ = std::integral_constant<size_t, I>;
template <class R, size_t N>
R partial_sum_(std::index_sequence<>, R, size_t_<N> ) {
return R{};
}
template <size_t I, size_t... Is, size_t... Js, size_t S>
auto partial_sum_(std::index_sequence<I, Is...>,
std::index_sequence<Js...>, size_t_<S> )
{
return partial_sum_(std::index_sequence<Is...>{},
std::index_sequence<Js..., S>{}, size_t_<S+I>{} );
}
template <size_t... Is>
auto partial_sum_(std::index_sequence<Is...> is)
{
return partial_sum_(is, std::index_sequence<>{}, size_t_<0>{} );
};
Which we can use to generate all of our indexes as a tuple:
template <size_t... Is, size_t N>
auto increment(std::index_sequence<Is...>, size_t_<N> )
{
return std::index_sequence<Is+N...>{};
}
template <class... Seqs, size_t... Ns>
auto make_all_indexes(std::index_sequence<Ns...>, Seqs... seqs)
{
return std::make_tuple(increment(seqs, size_t_<Ns>{})...);
}
Like so:
template <size_t... Is, class Tuple, class... Fs>
auto multiFunction(Tuple tuple, Fs... fs)
{
static_assert(sizeof...(Is) == sizeof...(Fs));
return multi_apply(tuple,
std::make_tuple(fs...),
make_all_indexes(
partial_sum_(std::index_sequence<Is...>{}),
std::make_index_sequence<Is>{}...
),
std::make_index_sequence<sizeof...(Is)>{}
);
}
If you want to handle void returns, then just make partial_apply return a tuple of a single element (or an empty tuple) and change the make_tuple() usage in multi_apply to tuple_cat().
Here's yet another impl:
template<std::size_t N>
constexpr Array<std::size_t, N> scan(std::size_t const (&a)[N])
{
Array<std::size_t, N> b{};
for (int i = 0; i != N - 1; ++i)
b[i + 1] = a[i] + b[i];
return b;
}
template<std::size_t O, std::size_t... N, class F, class Tuple>
inline decltype(auto) eval_from(std::index_sequence<N...>, F f, Tuple&& t)
{
return f(std::get<N + O>(std::forward<Tuple>(t))...);
}
template<std::size_t... O, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl1(std::index_sequence<O...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
return pack(eval_from<O>(std::make_index_sequence<N>(), f, std::forward<Tuple>(t))...);
}
template<std::size_t... I, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl0(std::index_sequence<I...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
constexpr std::size_t ns[] = {N...};
constexpr auto offsets = scan(ns);
return multi_function_impl1(std::index_sequence<offsets[I]...>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}
template<std::size_t... N, class Tuple, class... F>
auto multi_function(Tuple&& t, F... f)
{
return multi_function_impl0(std::make_index_sequence<sizeof...(N)>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}
where pack and Array are similar to std::make_tuple and std::array respectively, but to overcome some problems:
std::make_tuple decays it args, so references are lost
std::array cannot have its elems written in constexpr in c++14
DEMO
Here's my solution after following T.C.'s advice, adding to my previous (albeit inefficient) solution:
#include <iostream>
#include <tuple>
#include <utility>
struct NoReturnValue {
friend std::ostream& operator<< (std::ostream& os, const NoReturnValue&) {
return os << "[no value returned]";
}
};
template <std::size_t N, typename Tuple>
struct TupleHead {
static auto get (const Tuple& tuple) { // The subtuple from the first N components of tuple.
return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
}
};
template <typename Tuple>
struct TupleHead<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <std::size_t N, typename Tuple>
struct TupleTail {
static auto get (const Tuple& tuple) { // The subtuple from the last N components of tuple.
return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
}
};
template <typename Tuple>
struct TupleTail<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&,
std::enable_if_t< !std::is_void<std::result_of_t<F(std::tuple_element_t<Is, Tuple>...)>>::value >* = nullptr) { // This overload is called only if f's return type is not void.
return std::make_tuple(f(std::get<Is>(tuple)...)); // Thanks to T.C.'s advice on returning a single tuple and then calling std::tuple_cat on all the single tuples.
}
template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&,
std::enable_if_t< std::is_void<std::result_of_t<F(std::tuple_element_t<Is, Tuple>...)>>::value >* = nullptr) { // This overload is called only if f's return type is void.
f(std::get<Is>(tuple)...);
return std::tuple<NoReturnValue>(); // Thanks to T.C.'s advice on returning std::tuple<NoReturnValue>() if the return type of 'f' is void.
}
template <typename Tuple, typename F>
auto functionOnTuple (const Tuple& tuple, F f) {
return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}
template <typename Tuple, typename... Functions> struct MultiFunction;
template <typename Tuple, typename F, typename... Fs>
struct MultiFunction<Tuple, F, Fs...> {
template <std::size_t I, std::size_t... Is>
static inline auto execute (const Tuple& tuple, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
const auto headTuple = TupleHead<I, Tuple>::get(tuple);
const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
const auto r = functionOnTuple(headTuple, f); // Which overload of 'functionOnTupleHelper' is called dedends on whether f's return type is void or not.
return std::tuple_cat (r, MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, std::index_sequence<Is...>{}, fs...)); // T.C.'s idea of tuple_cat with all the single return tuples.
}
};
template <>
struct MultiFunction<std::tuple<>> {
static auto execute (const std::tuple<>&, std::index_sequence<>) { return std::tuple<>(); }
};
template <std::size_t... Is, typename Tuple, typename... Fs>
auto multiFunction (const Tuple& tuple, Fs... fs) {
return MultiFunction<Tuple, Fs...>::execute (tuple, std::index_sequence<Is...>{}, fs...);
}
// Testing
template <typename T> int foo (int, char) {std::cout << "foo<T>\n"; return 0;}
double bar (bool, double, long) {std::cout << "bar\n"; return 3.5;}
double bar (bool, int) {return 1.4;}
void voidFunction() {std::cout << "voidFunction\n";}
template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
const auto firstBar = [](bool b, double d, long l) {return bar(b, d, l);};
const auto t = multiFunction<2,3,0,3> (tuple, foo<bool>, firstBar, voidFunction, baz<2,5,1>); // Note that since 'bar' has an overload, we have to define 'firstBar' to indicate which 'bar' function we want to use.
std::cout << std::boolalpha << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << ' ' << std::get<3>(t) << '\n';
// 0 3.5 [no value returned] true
}
This solution should have linear time complexity. It uses std::tie instead of std::make_tuple, so neither the functions nor the arguments are copied unnecessarily. I think it should be fairly easy to follow compared to some other answers here.
First, we need a utility to invoke a function using a std::tuple of arguments.
template <typename F, typename Args, std::size_t... Is>
auto invoke_impl(F const& f, Args const& args, std::index_sequence<Is...>)
{
return f(std::get<Is>(args)...);
}
template <typename F, typename Args>
auto invoke(F const& f, Args const& args)
{
return invoke_impl(f, args, std::make_index_sequence<std::tuple_size<Args>::value>());
}
Secondly, we need a utility to std::tie a sub-range of tuple elements.
template <std::size_t Offset, typename Tuple, std::size_t... Is>
auto sub_tie_impl(Tuple const& tuple, std::index_sequence<Is...>)
{
return std::tie(std::get<Offset + Is>(tuple)...);
}
template <std::size_t Offset, std::size_t Count, typename Tuple>
auto sub_tie(Tuple const& tuple)
{
return sub_tie_impl<Offset>(tuple, std::make_index_sequence<Count>());
}
Now we can create our utility to consume a std::tuple of arguments using a sequence of functions.
First we std::tie the functions into a tuple, then we split the argument list into a parameter pack of sub-argument lists, and finally we invoke a function for each sub-argument list, packing the results into a tuple which we then return.
template <typename Fs, std::size_t... Is, typename... SubArgs>
auto consume_impl(Fs const& fs, std::index_sequence<Is...>, SubArgs const&... sub_args)
{
return std::make_tuple(invoke(std::get<Is>(fs), sub_args)...);
}
template <std::size_t, typename Args, typename Fs, typename... SubArgs>
auto consume_impl(Args const&, Fs const& fs, SubArgs const&... sub_args)
{
return consume_impl(fs, std::make_index_sequence<sizeof...(SubArgs)>(), sub_args...);
}
template <std::size_t Offset, std::size_t Count, std::size_t... Counts,
typename Args, typename Fs, typename... SubArgs>
auto consume_impl(Args const& args, Fs const& fs, SubArgs const&... sub_args)
{
return consume_impl<Offset + Count, Counts...>(args, fs, sub_args...,
sub_tie<Offset, Count>(args));
}
template <std::size_t... Counts, typename Args, typename... Fs>
auto consume(Args const& args, Fs const&... fs)
{
return consume_impl<0, Counts...>(args, std::tie(fs...));
}
Here's another solution borrowing Barry's partial_apply idea but avoiding the use of his partial_sum function altogether. It is shorter as a result. I think this is linear in time complexity.
#include <iostream>
#include <tuple>
#include <utility>
template <std::size_t Offset, typename F, typename Tuple, std::size_t... Is>
auto partial_apply_impl (F f, const Tuple& tuple, const std::index_sequence<Is...>&) {
return f(std::get<Offset + Is>(tuple)...);
}
template <typename Off, typename F, typename Tuple> // Off must be of type OffsetIndexSequence<A,B> only.
auto partial_apply (F f, const Tuple& tuple) {
return partial_apply_impl<Off::value>(f, tuple, typename Off::sequence{});
}
template <std::size_t Offset, std::size_t Size>
struct OffsetIndexSequence : std::integral_constant<std::size_t, Offset> {
using sequence = std::make_index_sequence<Size>;
};
template <typename Output, std::size_t... Is> struct OffsetIndexSequenceBuilder;
template <template <typename...> class P, typename... Out, std::size_t Offset, std::size_t First, std::size_t... Rest>
struct OffsetIndexSequenceBuilder<P<Out...>, Offset, First, Rest...> :
OffsetIndexSequenceBuilder<P<Out..., OffsetIndexSequence<Offset, First>>, Offset + First, Rest...> {};
template <template <typename...> class P, typename... Out, std::size_t Offset>
struct OffsetIndexSequenceBuilder<P<Out...>, Offset> {
using type = P<Out...>;
};
template <std::size_t... Is>
using offset_index_sequences = typename OffsetIndexSequenceBuilder<std::tuple<>, 0, Is...>::type;
template <typename> struct MultiFunction;
template <template <typename...> class P, typename... Offs>
struct MultiFunction<P<Offs...>> {
template <typename ArgsTuple, typename... Fs>
static auto execute (const ArgsTuple& argsTuple, Fs... fs) {
using ResultTuple = std::tuple<decltype(partial_apply<Offs>(fs, argsTuple))...>;
return ResultTuple{partial_apply<Offs>(fs, argsTuple)...};
}
};
template <std::size_t... Is, typename ArgsTuple, typename... Fs>
auto multiFunction (const ArgsTuple& argsTuple, Fs... fs) {
return MultiFunction<offset_index_sequences<Is...>>::execute(argsTuple, fs...);
}
// Testing
int foo (int, char) {std::cout << "foo\n"; return 0;}
double bar (bool, double, long) {std::cout << "bar\n"; return 3.5;}
bool baz (char, short, float) {std::cout << "baz\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
const std::tuple<int, double, bool> t = multiFunction<2,3,3> (tuple, foo, bar, baz); // foo bar baz
std::cout << std::boolalpha << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << '\n'; // 0 3.5 true
}

N-dimensional std::array initialized with templates

Consider the following code:
#include <iostream>
#include <array>
template <typename, int, int...> struct NArray;
template <typename T, int NUM_DIMENSIONS, int N>
struct NArray<T, NUM_DIMENSIONS, N> {
using type = std::array<T, N>;
};
template <typename T, int NUM_DIMENSIONS, int FIRST, int... REST>
struct NArray<T, NUM_DIMENSIONS, FIRST, REST...> {
using type = std::array<typename NArray<T, NUM_DIMENSIONS, REST...>::type, FIRST>;
};
template <typename T, int NUM_DIMENSIONS, int... N>
typename NArray<T, NUM_DIMENSIONS, N...>::type NDimensionalArray() {
typename NArray<T, NUM_DIMENSIONS, N...>::type nArray;
return nArray;
}
int main() {
const auto nArray = NDimensionalArray<int,4, 2,4,5,3>();
}
What I want is to be able to extend the template pack of NDimensionalArray with more int values so that certain values are initialized to some specified fixed value. For example,
auto a = NDimensionalArray<bool,4, 2,4,5,3, 1,2,3,2, 0,0,2,1>(true);
will return a 2x4x5x3 4-dimensional std::array with a[1][2][3][2] = true and a[0][0][2][1] = true, and every other element false. But I'm having issues with multiple template packs and can't seem to get it working. Any help would be appreciated. Thanks.
Well here's a working solution. If somebody can improve upon it, I would be very interested in seeing it because I don't know any other way to do it.
#include <iostream>
#include <array>
#include <cstring>
template <int... > struct seq {};
template <typename, int...> struct NArray;
template <typename T, int N>
struct NArray<T, N> {
using type = std::array<T, N>;
};
template <typename T, int FIRST, int... REST>
struct NArray<T, FIRST, REST...> {
using type = std::array<typename NArray<T, REST...>::type, FIRST>;
};
template <typename T, typename Dim>
struct make_narray;
template <typename T, int... N>
struct make_narray<T, seq<N...>>
{
using type = typename NArray<T, N...>::type;
};
template <typename T>
T& get(T& val, seq<>)
{
return val;
}
template <typename NA, int E0, int... Es>
auto get(NA& arr, seq<E0, Es...>)
-> decltype(get(arr[E0], seq<Es...>{}))
{
return get(arr[E0], seq<Es...>{});
}
template <typename T, typename Dim, typename... Elems>
typename make_narray<T, Dim>::type
NDimensionalArray(T val)
{
typename make_narray<T, Dim>::type narray{};
auto _{get(narray, Elems{}) = val ...}; // Quick initialization step!
return narray;
}
int main() {
auto a = NDimensionalArray<bool, seq<2, 4, 5, 3>, seq<1, 2, 3, 2>, seq<0, 0, 2, 1>>(true);
std::cout << std::boolalpha;
std::cout << a[0][0][0][0] << std::endl; // prints false
std::cout << a[1][2][3][2] << std::endl; // prints true
std::cout << a[0][0][2][1] << std::endl; // prints true
}
The exact syntax you wanted NDimensionalArray<bool,4, 2,4,5,3, 1,2,3,2, 0,0,2,1>(true), in both C++14 and C++11 (second demo):
#include <iostream>
#include <iomanip>
#include <array>
#include <tuple>
#include <utility>
#include <type_traits>
#include <cstddef>
template <typename, int, int...> struct NArray;
template <typename T, int NUM_DIMENSIONS, int N>
struct NArray<T, NUM_DIMENSIONS, N>
{
using type = std::array<T, N>;
};
template <typename T, int NUM_DIMENSIONS, int FIRST, int... REST>
struct NArray<T, NUM_DIMENSIONS, FIRST, REST...>
{
using type = std::array<typename NArray<T, NUM_DIMENSIONS, REST...>::type, FIRST>;
};
template <typename A, typename T>
void assign(A& arr, const T& value)
{
arr = value;
}
template <int I, int... Is, typename A, typename T>
void assign(A& arr, const T& value)
{
assign<Is...>(arr[I], value);
}
template <int SIZE, int PACK, int... Ind, typename T, typename A, std::size_t... Is>
auto set(const T& value, A& arr, std::index_sequence<Is...> seq)
-> std::enable_if_t<(SIZE*PACK == sizeof...(Ind))>
{
}
template <int SIZE, int PACK, int... Ind, typename T, typename A, std::size_t... Is>
auto set(const T& value, A& arr, std::index_sequence<Is...> seq)
-> std::enable_if_t<(SIZE*PACK < sizeof...(Ind))>
{
constexpr auto t = std::make_tuple(Ind...);
assign<std::get<PACK*SIZE+Is>(t)...>(arr, value);
set<SIZE, PACK+1, Ind...>(value, arr, seq);
}
template <typename T, int DIMS, int... N, std::size_t... Is>
auto make_narray(const T& value, std::index_sequence<Is...> seq)
{
constexpr auto t = std::make_tuple(N...);
typename NArray<T, DIMS, std::get<Is>(t)...>::type arr{};
set<DIMS, 1, N...>(value, arr, seq);
return arr;
}
template <typename T, int DIMS, int... N>
auto NDimensionalArray(const T& value)
{
return make_narray<T, DIMS, N...>(value, std::make_index_sequence<DIMS>{});
}
int main()
{
auto a = NDimensionalArray<bool,4, 2,4,5,3, 1,2,3,2, 0,0,2,1>(true);
std::cout << std::boolalpha;
std::cout << a[1][2][3][2] << std::endl; // ~~~~^
std::cout << a[0][0][2][1] << std::endl; // ~~~~~~~~~~~~^
std::cout << a[0][0][0][0] << std::endl; // (not set)
}
Output:
true
true
false
DEMO (C++14)
DEMO 2 (C++11)
Solution with the initializing positions in the argument pack ARGS&&... args instead:
#include <array>
#include <iostream>
#include <deque>
template <typename, std::size_t...> struct NArray;
template <typename T, std::size_t N>
struct NArray<T,N> {
using type = std::array<T,N>;
};
template <typename T, std::size_t First, std::size_t... Rest>
struct NArray<T, First, Rest...> {
using type = std::array<typename NArray<T, Rest...>::type, First>;
};
template <typename E, typename Container, typename T>
void assign (E& element, Container&&, const T& v) { element = v; }
template <typename Subarray, std::size_t N, typename Container, typename T>
void assign (std::array<Subarray, N>& narray, Container&& pos, const T& v) {
const std::size_t index = pos.front();
pos.pop_front();
assign (narray[index], pos, v);
}
template <typename T, int... Dimensions, typename... Args>
typename NArray<T, Dimensions...>::type NDimensionalArray (const T& value, Args&&... args) {
typename NArray<T, Dimensions...>::type narray{};
const auto initializer = {std::forward<Args>(args)...};
const int groupSize = sizeof...(Dimensions), numGroups = initializer.size() / groupSize;
for (std::size_t i = 0; i < numGroups; i++)
assign (narray, std::deque<std::size_t>(initializer.begin() + i*groupSize, initializer.begin() + (i+1)*groupSize), value);
return narray;
}
int main() {
const auto multiArray = NDimensionalArray<double, 5,6,7,8,9> (3.14, 1,2,3,2,4, 3,3,2,1,2, 0,1,3,1,2);
std::cout << multiArray[1][2][3][2][4] << '\n'; // 3.14
std::cout << multiArray[3][3][2][1][2] << '\n'; // 3.14
std::cout << multiArray[0][1][3][1][2] << '\n'; // 3.14
}
Here is Piotr's solution tidied up a bit, by removing his enable_if specializations and using the index trick once again instead. Also, I've generalized to the following example syntax for any number of set values:
makeNDimensionalArray<char, I<3,6,5,4>, I<2,4,3,2, 0,1,2,3, 1,2,4,3>, I<0,0,0,0, 2,3,1,2>, I<1,1,2,1>>('a','b','c')
where I<3,6,5,4> sets the multi-array's dimensions. Then I<2,4,3,2, 0,1,2,3, 1,2,4,3> sets those three indexed positions of the array to 'a', I<0,0,0,0, 2,3,1,2> sets those two indexed positions of the array to 'b', and so forth.
#include <iostream>
#include <array>
#include <tuple>
#include <utility>
template <typename, std::size_t, std::size_t...> struct NArray;
template <typename T, std::size_t NumDimensions, std::size_t N>
struct NArray<T, NumDimensions, N> {
using type = std::array<T, N>;
};
template <typename T, std::size_t NumDimensions, std::size_t First, std::size_t... Rest>
struct NArray<T, NumDimensions, First, Rest...> {
using type = std::array<typename NArray<T, NumDimensions, Rest...>::type, First>;
};
template <typename T, std::size_t... Dimensions>
using NDimensionalArray = typename NArray<T, sizeof...(Dimensions), Dimensions...>::type;
template <typename T, typename Dimensions> struct NArrayFromPack;
template <typename T, template <std::size_t...> class P, std::size_t... Dimensions>
struct NArrayFromPack<T, P<Dimensions...>> : NArray<T, sizeof...(Dimensions), Dimensions...> {
static constexpr std::size_t num_dimensions = sizeof...(Dimensions);
};
template <typename A, typename T>
void setArrayValue (A& a, const T& t) { a = t; }
template <std::size_t First, std::size_t... Rest, typename Array, typename T>
void setArrayValue (Array& array, const T& t) {
setArrayValue<Rest...>(array[First], t);
}
template <typename Indices, typename Sequence> struct InitializeArray;
template <template <std::size_t...> class P, std::size_t... Is, std::size_t... Js>
struct InitializeArray<P<Is...>, std::index_sequence<Js...>> {
template <typename Array, typename T>
static void execute (Array& array, const T& t) {
constexpr std::size_t GroupSize = sizeof...(Js), NumGroups = sizeof...(Is) / GroupSize;
set<GroupSize>(array, t, std::make_index_sequence<NumGroups>{});
}
private:
template <std::size_t GroupSize, typename Array, typename T, std::size_t... Ks>
static void set (Array& array, const T& t, std::index_sequence<Ks...>) {
const int dummy[] = {(do_set<Ks, GroupSize>(array, t), 0)...};
static_cast<void>(dummy);
}
template <std::size_t N, std::size_t GroupSize, typename Array, typename T>
static void do_set (Array& array, const T& t) {
constexpr std::size_t a[] = {Is...};
setArrayValue<a[N*GroupSize + Js]...>(array, t);
}
};
template <typename T, typename Dimensions, typename... Indices, typename... Args>
auto makeNDimensionalArray (const Args&... args) {
using A = NArrayFromPack<T, Dimensions>;
typename A::type array;
const int a[] = {(InitializeArray<Indices, std::make_index_sequence<A::num_dimensions>>::execute(array, args), 0)...};
static_cast<void>(a);
return array;
}
template <std::size_t...> struct I;
int main() {
const NDimensionalArray<char, 3,6,5,4> a = makeNDimensionalArray<char, I<3,6,5,4>, I<2,4,3,2, 0,1,2,3, 1,2,4,3>, I<0,0,0,0, 2,3,1,2>, I<1,1,2,1>>('a','b','c');
std::cout << a[2][4][3][2] << std::endl; // a
std::cout << a[0][1][2][3] << std::endl; // a
std::cout << a[1][2][4][3] << std::endl; // a
std::cout << a[0][0][0][0] << std::endl; // b
std::cout << a[2][3][1][2] << std::endl; // b
std::cout << a[1][1][2][1] << std::endl; // c
}

Get index of a tuple element's type?

If I have a tuple with different element types like
std::tuple<T0, T1, T2, ...>
And how to get the index of a element type?
template<class T, class Tuple>
struct Index
{
enum {value = ?;}
};
Thanks.
template <class T, class Tuple>
struct Index;
template <class T, class... Types>
struct Index<T, std::tuple<T, Types...>> {
static const std::size_t value = 0;
};
template <class T, class U, class... Types>
struct Index<T, std::tuple<U, Types...>> {
static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
};
See it live at Coliru.
This implementation returns the index of the first occurrence of a given type. Asking for the index of a type that is not in the tuple results in a compile error (and a fairly ugly one at that).
template< size_t I, typename T, typename Tuple_t>
constexpr size_t index_in_tuple_fn(){
static_assert(I < std::tuple_size<Tuple_t>::value,"The element is not in the tuple");
typedef typename std::tuple_element<I,Tuple_t>::type el;
if constexpr(std::is_same<T,el>::value ){
return I;
}else{
return index_in_tuple_fn<I+1,T,Tuple_t>();
}
}
template<typename T, typename Tuple_t>
struct index_in_tuple{
static constexpr size_t value = index_in_tuple_fn<0,T,Tuple_t>();
};
The example above avoids generating tons of sub tuples, which makes compilation fail (out of memory) when you call index_in_tuple for large tuples
With constexpr "function" (or lambda), you might do
template <class T, class Tuple>
struct Index;
template <class T, typename... Ts>
struct Index<T, std::tuple<Ts...>>
{
static constexpr std::size_t index = [](){
constexpr std::array<bool, sizeof...(Ts)> a{{ std::is_same<T, Ts>::value... }};
// You might easily handle duplicate index too (take the last, throw, ...)
// Here, we select the first one.
const auto it = std::find(a.begin(), a.end(), true);
// You might choose other options for not present.
// As we are in constant expression, we will have compilation error.
// and not a runtime expection :-)
if (it == a.end()) throw std::runtime_error("Not present");
return std::distance(a.begin(), it);
}();
};
Actually requires C++20 as missing constexpr for std functions,
but can easily be rewritten for previous version. (C++11 would be trickier with the strong restriction for constexpr).
Yet another one using fold expression.
It also sets the value to -1 when not found.
template <class X, class Tuple>
class Idx;
template <class X, class... T>
class Idx<X, std::tuple<T...>> {
template <std::size_t... idx>
static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
return -1 + ((std::is_same<X, T>::value ? idx + 1 : 0) + ...);
}
public:
static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
};
live: https://onlinegdb.com/SJE8kOYdv
EDIT:
As suggested by #Jarod42, one may use std::max:
template <class X, class Tuple>
class Idx;
template <class X, class... T>
class Idx<X, std::tuple<T...>> {
template <std::size_t... idx>
static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
return std::max({static_cast<ssize_t>(std::is_same_v<X, T> ? idx : -1)...});
}
public:
static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
};
template<typename X, class Tuple>
inline constexpr ssize_t Idx_v = Idx<X, Tuple>::value;
In case of duplicate type, this version returns the index of the last one.
live: https://onlinegdb.com/WenEBQs0L
template <typename T, typename U, typename... Us>
constexpr auto getIndex() {
if constexpr (is_same_v<T, U>) {
return 0;
} else {
if constexpr (sizeof...(Us)) {
return 1 + getIndex<T, Us...>();
} else {}
}
}
template <typename T, typename U, typename... Us>
constexpr auto getIndex(const tuple<U, Us...> &) {
return getIndex<T, U, Us...>();
}
usage
tuple the_tuple{'\0', 1, 2L, 3.0, "4", string{"5"}};
cout << getIndex<char>(the_tuple) << endl; // 0
cout << getIndex<double>(the_tuple) << endl; // 3
cout << getIndex<const char *>(the_tuple) << endl; // 4
cout << getIndex<string>(the_tuple) << endl; // 5
/* cout << getIndex<short>(the_tuple) << endl; // compile error */
Try this one, which reports error if the tuple is empty, T doesn't exist or not unique in the tuple:
template <template <typename ...> class TT, std::size_t I, typename ...Ts>
struct defer
{
using type = TT<I, Ts...>;
};
template <std::size_t, typename, typename>
struct tuple_index_helper;
template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index_helper<I, T, std::tuple<U, Vs...>>
{
static_assert(!std::is_same_v<T, U>, "Type not unique.");
static constexpr std::size_t index = tuple_index_helper<I, T, std::tuple<Vs...>>::index;
};
template <std::size_t I, typename T>
struct tuple_index_helper<I, T, std::tuple<>>
{
static constexpr std::size_t index = I;
};
template <std::size_t, typename, typename>
struct tuple_index;
template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index<I, T, std::tuple<U, Vs...>>
{
static constexpr std::size_t index = std::conditional_t<std::is_same_v<T, U>, defer<tuple_index_helper, I, T, std::tuple<Vs...>>, defer<tuple_index, I + 1, T, std::tuple<Vs...>>>::type::index;
};
template <std::size_t I, typename T>
struct tuple_index<I, T, std::tuple<>>
{
static_assert(!(I == 0), "Empty tuple.");
static_assert(!(I != 0), "Type not exist.");
};
template <typename T, typename U>
inline constexpr std::size_t tuple_index_v = tuple_index<0, T, U>::index;
Example:
std::tuple<int, float, const char*> t1{};
std::tuple<int, float, int> t2{};
std::tuple<> t3{};
constexpr auto idx = tuple_index_v<float, decltype(t1)>; // idx = 1
// constexpr auto idx2 = tuple_index_v<long long, decltype(t1)> // Error: Type not exist.
// constexpr auto idx3 = tuple_index_v<int, decltype(t2)> // Error: Type not unique.
// constexpr auto idx4 = tuple_index_v<int, decltype(t3)> // Error: Empty tuple.
This does what Qiang does, but it doesn't have that strange looking empty else branch.
It also makes sure that a tuple with unique types gets passed to it for good measure.
template <typename...>
inline constexpr auto is_unique = std::true_type{};
template <typename T, typename... Rest>
inline constexpr auto is_unique<T, Rest...> = std::bool_constant<(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>>{};
template <typename T, typename U, typename... Us>
constexpr auto getIndexImpl() {
if constexpr (std::is_same<T, U>::value) {
return 0;
} else {
static_assert(sizeof...(Us) > 0, "This tuple does not have that type");
return 1 + getIndexImpl<T, Us...>();
}
}
template <typename T, typename U, typename... Us>
constexpr auto getIndex(const std::tuple<U, Us...> &) {
static_assert(is_unique<U, Us...>, "getIndex should only be called on tuples with unique types.");
return getIndexImpl<T, U, Us...>();
}