Related
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.
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;
}
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
}
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
}
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...>();
}