constexpr initialization of array to sort contents - c++

This is a bit of a puzzle rather than a real-world problem, but I've gotten into a situation where I want to be able to write something that behaves exactly like
template<int N>
struct SortMyElements {
int data[N];
template<typename... TT>
SortMyElements(TT... tt) : data{ tt... }
{
std::sort(data, data+N);
}
};
int main() {
SortMyElements<5> se(1,4,2,5,3);
int se_reference[5] = {1,2,3,4,5};
assert(memcmp(se.data, se_reference, sizeof se.data) == 0);
}
except that I want the SortMyElements constructor to be constexpr.
Obviously this is possible for fixed N; for example, I can specialize
template<>
struct SortMyElements<1> {
int data[1];
constexpr SortMyElements(int x) : data{ x } {}
};
template<>
struct SortMyElements<2> {
int data[2];
constexpr SortMyElements(int x, int y) : data{ x>y?y:x, x>y?x:y } {}
};
But how do I generalize this into something that will work for any N?
Please notice that the array elements have to come from the actual values of the arguments, not from template non-type arguments; my elements come from constexpr expressions that, despite being evaluated at compile-time, reside firmly inside the "value system", rather than the "type system". (For example, Boost.MPL's sort works strictly within the "type system".)
I've posted a working "answer", but it's too inefficient to work for N > 6. I'd like to use this with 2 < N < 50 or thereabouts.
(P.S.— Actually what I'd really like to do is shuffle all the zeroes in an array to the end of the array and pack the nonzero values toward the front, which might be easier than full-on sorting; but I figure sorting is easier to describe. Feel free to tackle the "shuffle zeroes" problem instead of sorting.)

It's ugly, and probably not the best way to sort in a constant expression (because of the required instantiation depth).. but voilà, a merge sort:
Helper type, returnable array type with constexpr element access:
#include <cstddef>
#include <iterator>
#include <type_traits>
template<class T, std::size_t N>
struct c_array
{
T arr[N];
constexpr T const& operator[](std::size_t p) const
{ return arr[p]; }
constexpr T const* begin() const
{ return arr+0; }
constexpr T const* end() const
{ return arr+N; }
};
template<class T>
struct c_array<T, 0> {};
append function for that array type:
template<std::size_t... Is>
struct seq {};
template<std::size_t N, std::size_t... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<std::size_t... Is>
struct gen_seq<0, Is...> : seq<Is...> {};
template<class T, std::size_t N, class U, std::size_t... Is>
constexpr c_array<T, N+1> append_impl(c_array<T, N> const& p, U const& e,
seq<Is...>)
{
return {{p[Is]..., e}};
}
template<class T, std::size_t N, class U>
constexpr c_array<T, N+1> append(c_array<T, N> const& p, U const& e)
{
return append_impl(p, e, gen_seq<N>{});
}
Merge sort:
template<std::size_t Res, class T, class It, std::size_t Accum,
class = typename std::enable_if<Res!=Accum, void>::type >
constexpr c_array<T, Res> c_merge(It beg0, It end0, It beg1, It end1,
c_array<T, Accum> const& accum)
{
return
beg0 == end0 ? c_merge<Res>(beg0 , end0, beg1+1, end1, append(accum, *beg1)) :
beg1 == end1 ? c_merge<Res>(beg0+1, end0, beg1 , end1, append(accum, *beg0)) :
*beg0 < *beg1 ? c_merge<Res>(beg0+1, end0, beg1 , end1, append(accum, *beg0))
: c_merge<Res>(beg0 , end0, beg1+1, end1, append(accum, *beg1));
}
template<std::size_t Res, class T, class It, class... Dummies>
constexpr c_array<T, Res> c_merge(It beg0, It end0, It beg1, It end1,
c_array<T, Res> const& accum, Dummies...)
{
return accum;
}
template<class T, std::size_t L, std::size_t R>
constexpr c_array<T, L+R> c_merge(c_array<T, L> const& l,
c_array<T, R> const& r)
{
return c_merge<L+R>(l.begin(), l.end(), r.begin(), r.end(),
c_array<T, 0>{});
}
template<class T>
using rem_ref = typename std::remove_reference<T>::type;
template<std::size_t dist>
struct helper
{
template < class It >
static constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, dist>
{
return c_merge(helper<dist/2>::merge_sort(beg, beg+dist/2),
helper<dist-dist/2>::merge_sort(beg+dist/2, end));
}
};
template<>
struct helper<0>
{
template < class It >
static constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, 0>
{
return {};
}
};
template<>
struct helper<1>
{
template < class It >
static constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, 1>
{
return {*beg};
}
};
template < std::size_t dist, class It >
constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, dist>
{
return helper<dist>::merge_sort(beg, end);
}
Helpers for usage example:
template<class T, std::size_t N>
constexpr std::size_t array_size(T (&arr)[N]) { return N; }
template<class T, std::size_t N>
constexpr T* c_begin(T (&arr)[N]) { return arr; }
template<class T, std::size_t N>
constexpr T* c_end(T (&arr)[N]) { return arr+N; }
Usage example:
constexpr int unsorted[] = {5,7,3,4,1,8,2,9,0,6,10}; // odd number of elements
constexpr auto sorted = merge_sort<array_size(unsorted)>(c_begin(unsorted),
c_end(unsorted));
#include <iostream>
int main()
{
std::cout << "unsorted: ";
for(auto const& e : unsorted) std::cout << e << ", ";
std::cout << '\n';
std::cout << "sorted: ";
for(auto const& e : sorted) std::cout << e << ", ";
std::cout << '\n';
}
Output:
unsorted: 5, 7, 3, 4, 1, 8, 2, 9, 0, 6, 10,
sorted: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,

I know that this is an old question but as we have C++14 (and C++17 soon), and since C++14 constexpr rules aren't so restricted, and, for sure, couple of people will find your question in google, here is how quicksort (and of course other algorithms) can be done since C++14.
(big credits to #dyp for constexpr array)
#include <utility>
#include <cstdlib>
template<class T>
constexpr void swap(T& l, T& r)
{
T tmp = std::move(l);
l = std::move(r);
r = std::move(tmp);
}
template <typename T, size_t N>
struct array
{
constexpr T& operator[](size_t i)
{
return arr[i];
}
constexpr const T& operator[](size_t i) const
{
return arr[i];
}
constexpr const T* begin() const
{
return arr;
}
constexpr const T* end() const
{
return arr + N;
}
T arr[N];
};
template <typename T, size_t N>
constexpr void sort_impl(array<T, N> &array, size_t left, size_t right)
{
if (left < right)
{
size_t m = left;
for (size_t i = left + 1; i<right; i++)
if (array[i]<array[left])
swap(array[++m], array[i]);
swap(array[left], array[m]);
sort_impl(array, left, m);
sort_impl(array, m + 1, right);
}
}
template <typename T, size_t N>
constexpr array<T, N> sort(array<T, N> array)
{
auto sorted = array;
sort_impl(sorted, 0, N);
return sorted;
}
constexpr array<int, 11> unsorted{5,7,3,4,1,8,2,9,0,6,10}; // odd number of elements
constexpr auto sorted = sort(unsorted);
#include <iostream>
int main()
{
std::cout << "unsorted: ";
for(auto const& e : unsorted)
std::cout << e << ", ";
std::cout << '\n';
std::cout << "sorted: ";
for(auto const& e : sorted)
std::cout << e << ", ";
std::cout << '\n';
}
LIVE DEMO

A bit late to the party, but a much better and simpler implementation is the following comb_sort implementation.
template<typename Array>
constexpr void comb_sort_impl ( Array & array_ ) noexcept {
using size_type = typename Array::size_type;
size_type gap = array_.size ( );
bool swapped = false;
while ( ( gap > size_type { 1 } ) or swapped ) {
if ( gap > size_type { 1 } ) {
gap = static_cast<size_type> ( gap / 1.247330950103979 );
}
swapped = false;
for ( size_type i = size_type { 0 }; gap + i < static_cast<size_type> ( array_.size ( ) ); ++i ) {
if ( array_ [ i ] > array_ [ i + gap ] ) {
auto swap = array_ [ i ];
array_ [ i ] = array_ [ i + gap ];
array_ [ i + gap ] = swap;
swapped = true;
}
}
}
}
template<typename Array>
constexpr Array sort ( Array array_ ) noexcept {
auto sorted = array_;
comb_sort_impl ( sorted );
return sorted;
}
int main ( ) {
constexpr auto sorted = sort ( std::array<int, 8> { 6, 8, 0, 1, 5, 9, 2, 7 } );
for ( auto i : sorted )
std::cout << i << ' ';
std::cout << std::endl;
return EXIT_SUCCESS;
}
Output: 0 1 2 5 6 7 8 9
Why better, it's [the algorithm] often as good as insertion sort, but is non-recursive, which means it will work on any size arrays (at least not limited by the recursive depth).

Well, I got my inefficient version to compile, at least with Clang on OSX. Here's the code.
However, while it's tolerably fast for five elements, on my laptop it takes 0.5 seconds to sort six elements and 7 seconds to sort seven elements. (Catastrophically varying performance, too, depending on whether the items are almost-sorted or reverse-sorted.) I didn't even try timing eight. Clearly, this doesn't scale to the kind of things I want to do with it. (I'd say 50 elements is a reasonable upper bound for my contrived use-case, but 6 is unreasonably tiny.)
#include <cstring>
#include <cassert>
template<int...>
struct IntHolder {};
// Now let's make a consecutive range of ints from [A to B).
template<int A, int B, int... Accum>
struct IntRange_ : IntRange_<A+1, B, Accum..., A> {};
template<int A, int... Accum>
struct IntRange_<A, A, Accum...> {
using type = IntHolder<Accum...>;
};
template<int A, int B>
using IntRange = typename IntRange_<A,B>::type;
// And a helper function to do what std::min should be doing for us.
template<typename... TT> constexpr int min(TT...);
constexpr int min(int i) { return i; }
template<typename... TT> constexpr int min(int i, TT... tt) { return i < min(tt...) ? i : min(tt...); }
template<int N>
struct SortMyElements {
int data[N];
template<int... II, typename... TT>
constexpr SortMyElements(IntHolder<II...> ii, int minval, int a, TT... tt) : data{
( a==minval ? a : SortMyElements<N>(ii, minval, tt..., a).data[0] ),
( a==minval ? SortMyElements<N-1>(tt...).data[II] : SortMyElements<N>(ii, minval, tt..., a).data[II+1] )...
} {}
template<typename... TT>
constexpr SortMyElements(TT... tt) : SortMyElements(IntRange<0,sizeof...(tt)-1>(), min(tt...), tt...) {}
};
template<>
struct SortMyElements<1> {
int data[1];
constexpr SortMyElements(int x) : data{ x } {}
constexpr SortMyElements(IntHolder<>, int minval, int x) : SortMyElements(x) {}
};
static_assert(SortMyElements<5>(5,2,1,3,1).data[0] == 1, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[1] == 1, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[2] == 2, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[3] == 3, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[4] == 5, "");
char global_array[ SortMyElements<5>(1,4,2,5,3).data[2] ];
static_assert(sizeof global_array == 3, "");
int main() {
SortMyElements<5> se(1,4,2,5,3);
int se_reference[5] = {1,2,3,4,5};
assert(memcmp(se.data, se_reference, sizeof se.data) == 0);
}
UPDATE: I haven't figured out how to do a fast mergesort (although DyP's answer looks potentially feasible to me). However, this morning I did solve my original puzzle-problem of shuffling zeroes to the end of an array! I used a recursive partition-and-merge algorithm; the code looks like this.

Starting with C++20, all you need to change in your example is to add constexpr to the constructor. That is, in C++20, std::sort is in fact constexpr.

Related

Cartesian product for multiple sets at compile time

I am struggling with an implementation of the Cartesian product for
multiple indices with a given range 0,...,n-1.
The basic idea is to have a function:
cartesian_product<std::size_t range, std::size_t sets>()
with an output array that contains tuples that hold the different products
[(0,..,0), (0,...,1), (0,...,n-1),...., (n-1, ..., n-1)]
An simple example would be the following:
auto result = cartesian_product<3, 2>();
with the output type std::array<std::tuple<int, int>, (3^2)>:
[(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
My main problem is that my version of the Cartesian product is slow and creates a stack overflow if you choose to have more than 5 sets. I believe that my code has too many recursions and temporary variables.
My implementation (C++17) can be found here: cartesian_product
#include <stdio.h>
#include <iostream>
#include <tuple>
template<typename T, std::size_t ...is>
constexpr auto flatten_tuple_i(T tuple, std::index_sequence<is...>) {
return std::tuple_cat(std::get<is>(tuple)...);
}
template<typename T>
constexpr auto flatten_tuple(T tuple) {
return flatten_tuple_i(tuple, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template<std::size_t depth, typename T>
constexpr auto recursive_flatten_tuple(T tuple){
if constexpr(depth <= 1){
return tuple;
}else{
return recursive_flatten_tuple<depth-1>(flatten_tuple(tuple));
}
}
template<std::size_t depth, typename T, std::size_t ...is>
constexpr auto wdh(T&& tuple, std::index_sequence<is...>){
if constexpr (depth == 0) {
return tuple;
}else{
//return (wdh<depth-1>(std::tuple_cat(tuple, std::make_tuple(is)),std::make_index_sequence<sizeof...(is)>{})...);
return std::make_tuple(wdh<depth-1>(std::tuple_cat(tuple, std::make_tuple(is)), std::make_index_sequence<sizeof...(is)>{})...);
}
}
template<std::size_t sets, typename T, std::size_t ...is>
constexpr auto to_array(T tuple, std::index_sequence<is...>){
if constexpr (sets == 0){
auto t = (std::make_tuple(std::get<is>(tuple)),...);
std::array<decltype(t), sizeof...(is)> arr = {std::make_tuple(std::get<is>(tuple))...};
//decltype(arr)::foo = 1;
return arr;
}else{
auto t = ((std::get<is>(tuple)),...);
std::array<decltype(t), sizeof...(is)> arr = {std::get<is>(tuple)...};
return arr;
}
}
template<std::size_t sets, std::size_t ...is>
constexpr auto ct_i(std::index_sequence<is...>){
if constexpr (sets == 0){
auto u = std::tuple_cat(wdh<sets>(std::make_tuple(is), std::make_index_sequence<sizeof...(is)>{})...);
auto arr = to_array<sets>(u, std::make_index_sequence<std::tuple_size<decltype(u)>::value>{});
return arr;
}else {
auto u = std::tuple_cat(wdh<sets>(std::make_tuple(is), std::make_index_sequence<sizeof...(is)>{})...);
auto r = recursive_flatten_tuple<sets>(u);
auto d = to_array<sets>(r, std::make_index_sequence<std::tuple_size<decltype(r)>::value>{});
return d;
}
}
template<std::size_t range, std::size_t sets>
constexpr auto cartesian_product(){
static_assert( (range > 0), "lowest input must be cartesian<1,1>" );
static_assert( (sets > 0), "lowest input must be cartesian<1,1>" );
return ct_i<sets-1>(std::make_index_sequence<range>{});
}
int main()
{
constexpr auto crt = cartesian_product<3, 2>();
for(auto&& ele : crt){
std::cout << std::get<0>(ele) << " " << std::get<1>(ele) << std::endl;
}
return 0;
}
Since I was also working on a solution I thought I post it aswell (although very similar to Artyer's answer). Same premise, we replace the tuple with an array and just iterate over the elements, incrementing them one by one.
Note that the power function is broken, so if you need power values <0 or non-integer types you have to fix it.
template <typename It, typename T>
constexpr void setAll(It begin, It end, T value)
{
for (; begin != end; ++begin)
*begin = value;
}
template <typename T, std::size_t I>
constexpr void increment(std::array<T, I>& counter, T max)
{
for (auto idx = I; idx > 0;)
{
--idx;
if (++counter[idx] >= max)
{
setAll(counter.begin() + idx, counter.end(), 0); // because std::fill is not constexpr yet
}
else
{
break;
}
}
}
// std::pow is not constexpr
constexpr auto power = [](auto base, auto power)
{
auto result = base;
while (--power)
result *= base;
return result;
};
template<std::size_t range, std::size_t sets>
constexpr auto cartesian_product()
{
std::array<std::array<int, sets>, power(range, sets)> products{};
std::array<int, sets> currentSet{};
for (auto& product : products)
{
product = currentSet;
increment(currentSet, static_cast<int>(range));
}
return products;
}
int main()
{
constexpr auto crt = cartesian_product<5, 3>();
for (auto&& ele : crt)
{
for (auto val : ele)
std::cout << val << " ";
std::cout << "\n";
}
return 0;
}
Example
With Boost.Mp11, this is... alright, it's not a one-liner, but it's still not so bad:
template <typename... Lists>
using list_product = mp_product<mp_list, Lists...>;
template <typename... Ts>
constexpr auto list_to_tuple(mp_list<Ts...>) {
return std::make_tuple(int(Ts::value)...);
}
template <typename... Ls>
constexpr auto list_to_array(mp_list<Ls...>) {
return std::array{list_to_tuple(Ls{})...};
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using L = mp_repeat_c<mp_list<mp_iota_c<R>>, N>;
return list_to_array(mp_apply<list_product, L>{});
}
With C++20, you can declare the two helper function templates as lambdas inside of cartesian_product, which makes this read nicer (top to bottom instead of bottom to top).
Explanation of what's going on, based on the OP example of cartesian_product<3, 2>:
mp_iota_c<R> gives us the list [0, 1, 2] (but as integral constant types)
mp_repeat_c<mp_list<mp_iota_c<R>>, N> gives us [[0, 1, 2], [0, 1, 2]]. We just repeat the list, but we want a list of lists (hence the extra mp_list in the middle).
mp_apply<list_product, L> does mp_product, which is a cartesian product of all the lists you pass in... sticking the result in an mp_list. This gives you [[0, 0], [0, 1], [0, 2], ..., [2, 2]], but as an mp_list of mp_list of integral constants.
At this point the hard part is over, we just have to convert the result back to an array of tuples. list_to_tuple takes an mp_list of integral constants and turns that into a tuple<int...> with the right values. And list_to_array takes an mp_list of mp_lists of integral constants and turns that into an std::array of tuples.
A slightly different approach using just the single helper function:
template <template <typename...> class L,
typename... Ts, typename F>
constexpr auto unpack(L<Ts...>, F f) {
return f(Ts{}...);
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using P = mp_apply_q<
mp_bind_front<mp_product_q, mp_quote<mp_list>>,
mp_repeat_c<mp_list<mp_iota_c<R>>, N>>;
return unpack(P{},
[](auto... lists){
return std::array{
unpack(lists, [](auto... values){
return std::make_tuple(int(values)...);
})...
};
});
}
This approach is harder to read though, but it's the same algorithm.
You can do this without recursion easily. Notice that each tuple is the digits of numbers from 0 to range ** sets in base range, so you could increment a counter (Or apply to a std::index_sequence) and calculate each value one after the other.
Here's an implementation (That returns a std::array of std::arrays, which works mostly the same as std::tuples as you can get<N>, tuple_size and tuple_element<N> on a std::array, though if you really wanted you can convert them to std::tuples):
#include <cstddef>
#include <array>
namespace detail {
constexpr std::size_t ipow(std::size_t base, std::size_t exponent) noexcept {
std::size_t p = 1;
while (exponent) {
if (exponent % 2 != 0) {
p *= base;
}
exponent /= 2;
base *= base;
}
return p;
}
}
template<std::size_t range, std::size_t sets>
constexpr std::array<std::array<std::size_t, sets>, detail::ipow(range, sets)>
cartesian_product() noexcept {
constexpr std::size_t size = detail::ipow(range, sets);
std::array<std::array<std::size_t, sets>, size> result{};
for (std::size_t i = 0; i < size; ++i) {
std::size_t place = size;
for (std::size_t j = 0; j < sets; ++j) {
place /= range;
result[i][j] = (i / place) % range;
}
}
return result;
}
Here's a test link: https://www.onlinegdb.com/By_X9wbrI
Note that (empty_set)^0 is defined as a set containing an empty set here, but that can be changed by making ipow(0, 0) == 0 instead of 1
I was trying it out just for fun and I ended with pretty much the same idea as #Timo, just with a different format/style.
#include <iostream>
#include <array>
using namespace std;
template<size_t range, size_t sets>
constexpr auto cartesian_product() {
// how many elements = range^sets
constexpr auto size = []() {
size_t x = range;
size_t n = sets;
while(--n != 0) x *= range;
return x;
}();
auto products = array<array<size_t, sets>, size>();
auto counter = array<size_t, sets>{}; // array of zeroes
for (auto &product : products) {
product = counter;
// counter increment and wrapping/carry over
counter.back()++;
for (size_t i = counter.size()-1; i != 0; i--) {
if (counter[i] == range) {
counter[i] = 0;
counter[i-1]++;
}
else break;
}
}
return products;
}
int main() {
auto prods = cartesian_product<3, 6>();
}
I basically have a counter array which I increment manually, like so:
// given cartesian_product<3, 4>
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 1, 0] // carry over
...
...
[2, 2, 2, 2]
Pretty much just how you would do it by hand.
Example
If you want it in compile-time, you should only employ compile-time evaluations over compile-time data structures. As #Barry pointed above, using Boost.Mp11 greatly facilitates it. Of course you can do reimplement the relevant fundamental functions in plain C++17 on your own:
#include <iostream>
template<class T> struct Box {
using type = T;
};
template<class... Types> struct List {};
template<class Car, class Cdr> struct Cons;
template<class Car, class Cdr> using ConsT = typename Cons<Car, Cdr>::type;
template<class Car, class... Cdr> struct Cons<Car, List<Cdr...>>: Box<List<Car, Cdr...>> {};
using Nil = List<>;
template<std::size_t i, class L> struct Nth;
template<std::size_t i, class L> using NthT = typename Nth<i, L>::type;
template<std::size_t i, class... Ts> struct Nth<i, List<Ts...>>: std::tuple_element<i, std::tuple<Ts...>> {};
template<class L> struct Head;
template<class L> using HeadT = typename Head<L>::type;
template<class Car, class... Cdr> struct Head<List<Car, Cdr...>>: Box<Car> {};
template<class L> struct Tail;
template<class L> using TailT = typename Tail<L>::type;
template<class Car, class... Cdr> struct Tail<List<Car, Cdr...>>: Box<List<Cdr...>> {};
template<class... Lists> struct Concat;
template<class... Lists> using ConcatT = typename Concat<Lists...>::type;
template<class T, class... Rest> struct Concat<T, Rest...>: Cons<T, ConcatT<Rest...>> {};
template<class Head, class... Tail, class... Rest> struct Concat<List<Head, Tail...>, Rest...>: Cons<Head, ConcatT<List<Tail...>, Rest...>> {};
template<class... Rest> struct Concat<Nil, Rest...>: Concat<Rest...> {};
template<> struct Concat<>: Box<Nil> {};
template<class T, class Subspace> struct Prepend;
template<class T, class Subspace> using PrependT = typename Prepend<T, Subspace>::type;
template<class T, class... Points> struct Prepend<T, List<Points...>>: Box<List<ConsT<T, Points>...>> {};
template<class T> struct Prepend<T, Nil>: Box<List<List<T>>> {};
template<class Range, class Subspace> struct Product;
template<class Range, class Subspace> using ProductT = typename Product<Range, Subspace>::type;
template<class Range, class Subspace> struct Product: Concat<PrependT<HeadT<Range>, Subspace>, ProductT<TailT<Range>, Subspace>> {};
template<class Subspace> struct Product<Nil, Subspace>: Box<Nil> {};
template<std::size_t i> using IntValue = std::integral_constant<std::size_t, i>;
template<class Seq> struct IntegerSequence;
template<class Seq> using IntegerSequenceT = typename IntegerSequence<Seq>::type;
template<std::size_t... is> struct IntegerSequence<std::index_sequence<is...>>: Box<List<IntValue<is>...>> {};
template<std::size_t n> using Range = IntegerSequenceT<std::make_index_sequence<n>>;
template<std::size_t dimensions, std::size_t range> struct CartesianCube;
template<std::size_t dimensions, std::size_t range> using CartesianCubeT = typename CartesianCube<dimensions, range>::type;
template<std::size_t dimensions, std::size_t range> struct CartesianCube: Product<Range<range>, CartesianCubeT<dimensions - 1, range>> {};
template<std::size_t range> struct CartesianCube<0, range>: Box<Nil> {};
template<std::size_t i> std::ostream &operator<<(std::ostream &s, IntValue<i>) {
return s << '<' << i << '>';
}
template<class... Ts> std::ostream &operator<<(std::ostream &s, List<Ts...>);
namespace detail_ {
template<class L, std::size_t... is> std::ostream &printList(std::ostream &s, L, std::index_sequence<is...>) {
return ((s << (is == 0? "" : ", ") << NthT<is, L>{}), ...), s;
}
}
template<class... Ts> std::ostream &operator<<(std::ostream &s, List<Ts...>) {
return detail_::printList(s << "List{", List<Ts...>{}, std::index_sequence_for<Ts...>{}) << '}';
}
int main() {
std::cout << CartesianCubeT<2, 3>{} << '\n';
}
Note that CartesianCubeT here is actually a List of Lists of integral_constants. Once you have those, converting them into run-time values is trivial. Note that cartesian_product does not even have to be a function, since the whole data set is evaluated at compile-time it can be a templated value.

Constexpr determinant (2 dimensional std::array)

I need to write a constexpr function that computes a determinant at compile time. The most obvious solution is to use Laplace expansion. C++14 is supported.
#include <array>
#include <utility>
constexpr int get_cofactor_coef(int i, int j) {
return (i + j) % 2 == 0 ? 1 : -1;
}
template <int N>
constexpr int determinant(const std::array<std::array<int, N>, N>& a) {
int det = 0;
for (size_t i = 0u; i < N; ++i) {
det += get_cofactor_coef(i, 1) * a[i][0] * determinant<N-1>(GET_SUBMATRIX_OF_A<N-1, I, J>(a);
}
return det;
}
template <>
constexpr int determinant<2>(const std::array<std::array<int, 2>, 2>& a) {
return a[0][0] * a[1][1] - a[0][1] * a[1][0];
}
template <>
constexpr int determinant<1>(const std::array<std::array<int, 1>, 1>& a) {
return a[0][0];
}
The problem is that I have absolutely no clue how to write the GET_SUBMATRIX_OF_A.
I know that I need to:
Generate a sequence (using std::integer_sequence probably);
Exclude from this sequence the i-th row;
Copy all but the first (0-th) column;
My constexpr skills are next to non-existent. Head on attempts to pass a to another function result in weird errors like error: '* & a' is not a constant expression.
All help is greatly appreciated!
The problem is that the non-const std::array<T, N>::operator[] (returning T&) is not constexpr until C++17, making it difficult to set the elements of the minor.
However, there is an escape hatch, which is that std::get<I>(std::array&) is constexpr, and it is perfectly legitimate to perform pointer arithmetic on the result, so we can rewrite
a[i] // constexpr since C++17
as
(&std::get<0>(a))[i] // constexpr in C++14!!
That is, we use std::get to obtain a constexpr reference to the first member of the array, take a pointer to it, and use the built-in [] operator on the pointer and index.
Then a two-level array member access a[i][j] becomes the horrendously ugly but still constexpr (&std::get<0>((&std::get<0>(a))[i]))[j], meaning we can write get_submatrix_of_a as an ordinary constexpr function:
template<std::size_t N>
constexpr std::array<std::array<int, N - 1>, N - 1>
get_submatrix_of_a(const std::array<std::array<int, N>, N>& a, int i, int j) {
std::array<std::array<int, N - 1>, N - 1> r{};
for (int ii = 0; ii != N - 1; ++ii)
for (int jj = 0; jj != N - 1; ++jj)
(&std::get<0>(((&std::get<0>(r))[ii])))[jj] = a[ii + (ii >= i ? 1 : 0)][jj + (jj >= j ? 1 : 0)];
return r;
}
Remember that const std::array<T, N>::operator[] is already constexpr in C++14, so we don't need to rewrite the RHS of the minor construction.
Here's an example implementation. It might be possible to do this even shorter or more elegant, but it's a starting point. Actually, I just realized your matrices are square, so it's definitely possible to drop some template parameters in the code below.
As I mentioned in my comment, for C++17 and up, it's very likely none of this is required at all.
First, let's define some boilerplate that let's us create and index sequence with one value left out (i.e. the row you want to skip):
#include <utility>
// Based on https://stackoverflow.com/a/32223343.
template <size_t Offset, class T1, class T2>
struct offset_sequence_merger;
template <size_t Offset, size_t... I1, size_t... I2>
struct offset_sequence_merger<Offset, std::index_sequence<I1...>, std::index_sequence<I2...>>
: std::index_sequence<I1..., (Offset + I2)...>
{ };
template <std::size_t Excluded, std::size_t End>
using make_excluded_index_sequence = offset_sequence_merger<Excluded + 1,
std::make_index_sequence<Excluded>,
std::make_index_sequence<End - Excluded - 1>>;
Now let's put this to use to extract sub-matrices:
#include <array>
template <class T, std::size_t N, std::size_t... Indices>
constexpr std::array<T, sizeof...(Indices)> extract_columns (
std::array<T, N> const & source, std::index_sequence<Indices...>) {
return { source.at(Indices)... };
}
template <class T, std::size_t N>
constexpr std::array<T, N - 1> drop_first_column (
std::array<T, N> const & source) {
return extract_columns(source, make_excluded_index_sequence<0, N>());
}
template <class T, std::size_t Rows, std::size_t Cols, std::size_t... RowIndices>
constexpr auto create_sub_matrix (
std::array<std::array<T, Cols>, Rows> const & source,
std::index_sequence<RowIndices...>)
-> std::array<std::array<T, Cols - 1>, sizeof...(RowIndices)> {
return { drop_first_column(source.at(RowIndices))... };
}
template <std::size_t ExcludedRow, class T, std::size_t Rows, std::size_t Cols>
constexpr auto create_sub_matrix (
std::array<std::array<T, Cols>, Rows> const & source)
-> std::array<std::array<T, Cols - 1>, Rows - 1> {
return create_sub_matrix(source,
make_excluded_index_sequence<ExcludedRow, Rows>());
}
And finally, here's some code showing that the above seems to do what it should. You can see it in action at Wandbox:
#include <iostream>
#include <string>
template <class T>
void print_seq (std::integer_sequence<T> const & /* seq */) {
std::cout << '\n';
}
template <class T, T Head, T... Tail>
void print_seq (std::integer_sequence<T, Head, Tail...> const & /* seq */) {
std::cout << Head << ' ';
print_seq(std::integer_sequence<T, Tail...>{});
}
template <class T, std::size_t N>
void print_array (std::array<T, N> const & src) {
std::string sep = "";
for (auto const & e : src) {
std::cout << sep << e;
sep = " ";
}
std::cout << '\n';
}
template <class T, std::size_t N, std::size_t M>
void print_matrix (std::array<std::array<T, N>, M> const & src) {
for (auto const & row : src) { print_array(row); }
}
int main () {
auto indexSeqA = make_excluded_index_sequence<0, 3>(); print_seq(indexSeqA);
auto indexSeqB = make_excluded_index_sequence<1, 3>(); print_seq(indexSeqB);
auto indexSeqC = make_excluded_index_sequence<2, 3>(); print_seq(indexSeqC);
std::cout << '\n';
std::array<int, 3> arr = { 1, 7, 9 };
print_array(arr); std::cout << '\n';
std::array<std::array<int, 3>, 3> matrix = {{
{ 0, 1, 2 }
, { 3, 4, 5 }
, { 6, 7, 8 }
}};
print_matrix(matrix); std::cout << '\n';
print_matrix(create_sub_matrix<0>(matrix)); std::cout << '\n';
print_matrix(create_sub_matrix<1>(matrix)); std::cout << '\n';
}
Hopefully that's enough to help you implement the determinant function completely. (P.S.: No need to explicitly provide the size_t template argument to determinant when calling it, it will be automatically deduced from the size of it's std::array argument).

Compiletime transform of tree into tuple

I'm stuck with the following problem:
given a tree represented by non-terminal nodes of type Node<> and terminal nodes of arbitrary types like A, B and so on (see below).
Because I don't want to use runtime-polymorphism I like to transform the tree into a std::tuple via a constexpr function like the immediately invoked lambda expression in the example below.
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
template<typename... T>
struct Node {
constexpr Node(const T&... n) : mChildren{n...} {}
std::tuple<T...> mChildren;
};
template<uint8_t N>
struct IndexNode {
std::array<uint8_t, N> mChildren;
};
int main() {
constexpr auto tree = []() {
auto t = Node(A(),
B(),
Node(C(),
Node(D())),
E());
// transform t into std::tuple<A, B, C, D, IndexNode<1>{3}, IndexNode<2>{2, 4}, E, IndexNode<4>{0, 1, 5, 6}>
// return ...;
}();
}
The idea is to use an index to a tuple element as a "pointer" to the active (selected) node of the tree. The overall purpose is to implement a menu-system on a µC without using runtime-polymorphism.
If I can carry out this transformation at compiletime, I can use a special meta-function to retrieve the active tuple-element and call some function on it. This function I wrote already.
The missing link would surely be some sort of depth-first tree-traversal ... but I can't figure it out.
What about using a lot of std::tuple_cat, std::index_sequence and recursion as follows?
#include <tuple>
#include <array>
#include <iostream>
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
template <typename... T>
struct Node
{
constexpr Node (T const & ... n) : mChildren { n... }
{ }
std::tuple<T...> mChildren;
};
template <std::size_t N>
struct IndexNode
{ std::array<uint8_t, N> mChildren; };
template <typename>
struct cntT : public std::integral_constant<std::size_t, 1U>
{ };
template <typename ... Ts>
struct cntT<Node<Ts...>>
: public std::integral_constant<std::size_t, 1U + (cntT<Ts>::value + ...)>
{ };
template <typename T>
struct getT
{
constexpr auto operator() (T const & t, std::size_t & cnt)
{ ++cnt; return std::make_tuple(t); }
};
template <typename ... Ts>
struct getT<Node<Ts...>>
{
template <std::size_t ... Is>
constexpr auto func (std::tuple<Ts...> const & t,
std::index_sequence<Is...> const &,
std::size_t & cnt)
{
std::size_t val { cnt };
IndexNode<sizeof...(Ts)> in
{ { { uint8_t(val += cntT<Ts>::value)... } } };
return std::tuple_cat(getT<Ts>()(std::get<Is>(t), cnt)...,
std::make_tuple(in));
}
constexpr auto operator() (Node<Ts...> const & n, std::size_t & cnt)
{
return func(n.mChildren, std::make_index_sequence<sizeof...(Ts)>{},
cnt);
}
};
template <typename ... Ts>
constexpr auto linearNode (Node<Ts...> const & n)
{
std::size_t cnt ( -1 );
return getT<Node<Ts...>>()(n, cnt);
}
int main()
{
constexpr auto tree = []()
{
auto t = Node { A{}, B{}, Node{ C{}, Node{ D{} } }, E{} };
return linearNode(t);
}();
static_assert( std::is_same<
decltype(tree),
std::tuple<A, B, C, D, IndexNode<1>, IndexNode<2>, E,
IndexNode<4>> const>::value, "!");
std::cout << "IndexNode<1> { ";
for ( auto const & v : std::get<4U>(tree).mChildren )
std::cout << int(v) << ", ";
std::cout << "}" << std::endl; // print IndexNode<1> { 3, }
std::cout << "IndexNode<2> { ";
for ( auto const & v : std::get<5U>(tree).mChildren )
std::cout << int(v) << ", ";
std::cout << "}" << std::endl; // print IndexNode<2> { 2, 4, }
std::cout << "IndexNode<4> { ";
for ( auto const & v : std::get<7U>(tree).mChildren )
std::cout << int(v) << ", ";
std::cout << "}" << std::endl; // print IndexNode<4> { 0, 1, 5, 6, }
}
#wimalopaan did you read max66' answer or did you find another solution for your idea? I tried to tackle the problem by connecting input and output via an index mapping. However, that became more complicated than I expected. Here is how I trid to establish the index mapping:
For the output tuple, there is an obvious choice of indices. Swapping the storage order a bit (which is simpler for me to imagine), the indices read
using Tree = std::tuple<
IndexNode<4>{1, 2, 3, 7},// 0
A, // 1
B, // 2
IndexNode<2>{4, 5}, // 3
C, // 4
IndexNode<1>{6}, // 5
D, // 6
E // 7
>;
The input consists of nested tuples, so let us invent some multiindex:
Node(/* 1, 2, 3, 7 */ // 0, Vals<>
A{}, // 1, Vals<0>
B{}, // 2, Vals<1>
Node(/* 4, 5 */ // 3, Vals<2>
C{}, // 4, Vals<2, 0>
Node(/* 6 */ // 5, Vals<2, 1>
D{} // 6, Vals<2, 1, 0>
)
),
E{} // 7, Vals<3>
);
For computing the indices in IndexNode it is useful to specify the "number of output indices consumed by a Node including its children". In max66's answer this is called cntT. Let us use the term rank for this quantity here and compute by function overload:
template<class> struct Type {};// helper to pass a type by value (for overloading)
template<class Terminal>
size_t rank_of(Type<Terminal>) {// break depth recursion for terminal node
return 1;
}
template<class... Children>
size_t rank_of(Type<Node<Children...>>) {// continue recursion for non-terminal node
return (
1 +// count enclosing non-terminal node
... + rank_of(Type<Children>{})
);
}
The same strategy can be applied to obtain the multiindices of each node in the input representation. The multiindices are accumulated (by depth recursion) into ParentInds.
#include "indexer.hpp"// helper for the "indices trick"
#include "merge.hpp"// tuple_cat for types
#include "types.hpp"// template<class...> struct Types {};
#include "vals.hpp"// helper wrapped around std::integer_sequence
template<class Terminal, class ParentInds=Vals<>>
auto indices_of(
Type<Terminal>,// break depth recursion for terminal node
ParentInds = ParentInds{}
) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return Types<ParentInds>{};// wrap in Types<...> for simple merging
}
template<class... Children, class ParentInds=Vals<>>
auto indices_of(
Type<Node<Children...>>,// continue recursion for non-terminal node
ParentInds parent_inds = ParentInds{}
) {
return indexer<Children...>([&] (auto... child_inds) {
return merge(
Types<ParentInds>{},// indices for enclosing non-terminal node
indices_of(
Type<Children>{},
parent_inds.append(child_inds)
)...
);
});
}
Output with GCC 7.2:
auto indices_of(Type<Terminal>, ParentInds) [with Terminal = E; ParentInds = Vals<3>]
auto indices_of(Type<Terminal>, ParentInds) [with Terminal = D; ParentInds = Vals<2, 1, 0>]
auto indices_of(Type<Terminal>, ParentInds) [with Terminal = C; ParentInds = Vals<2, 0>]
auto indices_of(Type<Terminal>, ParentInds) [with Terminal = B; ParentInds = Vals<1>]
auto indices_of(Type<Terminal>, ParentInds) [with Terminal = A; ParentInds = Vals<0>]
With the index mapping computed by indices_of we can construct the output tuple like this:
template<class T>
constexpr auto transform(const T& t) {
static_assert(
std::is_same<
T,
Node<A, B, Node<C, Node<D> >, E>
>{}
);
auto inds = indices_of(Type<T>{});
static_assert(
std::is_same<
decltype(inds),
Types<
Vals<>,
Vals<0>,
Vals<1>,
Vals<2>,
Vals<2, 0>,
Vals<2, 1>,
Vals<2, 1, 0>,
Vals<3>
>
>{}
);
return indexer(inds.size, [&] (auto... is) {// `is` are the tuple's output inds
return std::make_tuple(// becomes the final output tuple
transform_at(
inds.construct_type_at(is),// multiindex `Vals<...>{}` for each tuple element
t,// input tree
is.next()// used by each `IndexNode`: offset for its index computation
)...
);
});
}
Here is a complete (hopefully bug-free) working example (online demo):
#include <algorithm>
#include <iostream>
#include <tuple>
#include <numeric>
////////////////////////////////////////////////////////////////////////////////
template<size_t i>
struct Val : std::integral_constant<size_t, i> {
template<size_t dist=1>
constexpr auto next(Val<dist> = {}) {
return Val<i+dist>{};
}
};
template<size_t... is>
struct Vals {
template<size_t i>
constexpr auto append(Val<i>) {
return Vals<is..., i>{};
}
};
template<size_t i0, size_t... is>
constexpr auto front(Vals<i0, is...>) { return Val<i0>{}; }
template<size_t i0, size_t... is>
constexpr auto pop_front(Vals<i0, is...>) { return Vals<is...>{}; }
////////////////////////////////////////////////////////////////////////////////
template<class> struct Type {};
template<class... Ts>
struct Types {
static constexpr auto size = Val<sizeof...(Ts)>{};
template<std::size_t i, class... Args>
constexpr auto construct_type_at(Val<i>, Args&&... args) {
using Ret = std::tuple_element_t<i, std::tuple<Ts...>>;
return Ret(std::forward<Args>(args)...);
}
};
////////////////////////////////////////////////////////////////////////////////
template<std::size_t... is, class F>
constexpr decltype(auto) indexer_impl(std::index_sequence<is...>, F f) {
return f(Val<is>{}...);
}
template<class... Ts, class F>
constexpr decltype(auto) indexer(F f) {
return indexer_impl(std::index_sequence_for<Ts...>{}, f);
}
template<size_t N, class F>
constexpr decltype(auto) indexer(std::integral_constant<size_t, N>, F f) {
return indexer_impl(std::make_index_sequence<N>{}, f);
}
////////////////////////////////////////////////////////////////////////////////
template<class... Ts>
auto merge(Types<Ts...> done) {
return done;
}
template<class... Ts, class... Us, class... Vs>
auto merge(Types<Ts...>, Types<Us...>, Vs...) {
// TODO: if desired, switch to logarithmic recursion
// https://stackoverflow.com/a/46934308/2615118
return merge(Types<Ts..., Us...>{}, Vs{}...);
}
////////////////////////////////////////////////////////////////////////////////
struct TerminalNode { const char* msg{""}; };// JUST FOR DEBUG
std::ostream& operator<<(std::ostream& os, const TerminalNode& tn) {
return os << tn.msg << std::endl;// JUST FOR DEBUG
}
struct A : TerminalNode {};// INHERITANCE JUST FOR DEBUG
struct B : TerminalNode {};
struct C : TerminalNode {};
struct D : TerminalNode {};
struct E : TerminalNode {};
template<typename... T>
struct Node {
constexpr Node(const T&... n) : mChildren{n...} {}
std::tuple<T...> mChildren;
};
template<size_t N>
struct IndexNode {
std::array<size_t, N> mChildren;
constexpr IndexNode(std::array<size_t, N> arr) : mChildren(arr) {}
friend std::ostream& operator<<(std::ostream& os, const IndexNode& self) {
for(auto r : self.mChildren) os << r << ", ";// JUST FOR DEBUG
return os << "\n";
}
};
////////////////////////////////////////////////////////////////////////////////
template<class Terminal>
size_t rank_of(
Type<Terminal>// break depth recursion for terminal node
) {
return 1;
}
template<class... Children>
size_t rank_of(
Type<Node<Children...>>// continue recursion for non-terminal node
) {
return (
1 +// count enclosing non-terminal node
... + rank_of(Type<Children>{})
);
}
////////////////////////////////////////////////////////////////////////////////
template<class Terminal, class ParentInds=Vals<>>
auto indices_of(
Type<Terminal>,// break depth recursion for terminal node
ParentInds = ParentInds{}
) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return Types<ParentInds>{};// wrap in Types<...> for simple merging
}
template<class... Children, class ParentInds=Vals<>>
auto indices_of(
Type<Node<Children...>>,// continue recursion for non-terminal node
ParentInds parent_inds = ParentInds{}
) {
return indexer<Children...>([&] (auto... child_inds) {
return merge(
Types<ParentInds>{},// indices for enclosing non-terminal node
indices_of(
Type<Children>{},
parent_inds.append(child_inds)
)...
);
});
}
////////////////////////////////////////////////////////////////////////////////
template<class It, class T>
constexpr It exclusive_scan(It first, It last, It out, T init) {
for(auto it=first; it!=last; ++it) {
auto in = *it;
*out++ = init;
init += in;
}
return out;
}
////////////////////////////////////////////////////////////////////////////////
template<size_t... child_inds, class Terminal, class Offset>
constexpr decltype(auto) transform_at(
Vals<child_inds...> inds,
const Terminal& terminal,
Offset
) {
static_assert(0 == sizeof...(child_inds));
return terminal;
}
template<size_t... child_inds, class... Children, class Offset>
constexpr decltype(auto) transform_at(
Vals<child_inds...> inds,
const Node<Children...>& node,
Offset offset = Offset{}
) {
if constexpr(0 == sizeof...(child_inds)) {// the IndexNode is desired
auto ranks = std::array{rank_of(Type<Children>{})...};
exclusive_scan(std::begin(ranks), std::end(ranks), std::begin(ranks), 0);
auto add_offset = [] (size_t& i) { i += Offset{}; };
std::for_each(std::begin(ranks), std::end(ranks), add_offset);
return IndexNode{ranks};
}
else {// some child of this enclosing node is desired
return transform_at(
pop_front(inds),
std::get<front(inds)>(node.mChildren),
offset
);
}
}
template<class T>
constexpr auto transform(const T& t) {
auto inds = indices_of(Type<T>{});
return indexer(inds.size, [&] (auto... is) {// is are the tuple output inds
return std::make_tuple(// becomes the final output tuple
transform_at(
inds.construct_type_at(is),// multiindex for each tuple element
t,// input tree
is.next()// used by each `IndexNode`: offset for its index computation
)...
);
});
}
////////////////////////////////////////////////////////////////////////////////
int main() {
auto tree = []() {
auto t = Node( // 0, Val<>
A{"FROM A"}, // 1, Val<0>
B{"FROM B"}, // 2, Val<1>
Node( // 3, Val<2>
C{"FROM C"}, // 4, Val<2, 0>
Node( // 5, Val<2, 1>
D{"FROM D"} // 6, Val<2, 1, 0>
)
),
E{"FROM E"} // 7, Val<3>
);
return transform(t);
}();
using Tree = decltype(tree);
indexer(std::tuple_size<Tree>{}, [&] (auto... is) {
(std::cout << ... << std::get<is>(tree));
});
return 0;
}

Differentiate between 1D and 2D container in template class constructor (SFINAE)

So, I have a class, which has an array of arrays as a private member. I wish to have two constructors for each case (1D or 2D). But of course their declaration happens to be the same, so template deduction can't do its job without me doing something about it. Here's the code:
Edit: I also need it to work with STL containers like vector or C++ array. That is why I am overcomplicating and not going with the "arrays" fix.
#include <iostream>
#include <array>
template<class T, std::size_t rows_t, std::size_t cols_t>
class test
{
private:
std::array<std::array<T, cols_t>, rows_t> _data;
public:
auto begin() { return this->_data.begin(); }
auto end() { return this->_data.end(); }
//CONSTRUCTOR
template<class type_t>
test(const type_t &arr)
{
std::size_t j = 0;
for (const auto &num : arr)
this->_data[0][j++] = num;
}
template<class type_t>
test(const type_t &arr)
{
std::size_t i = 0;
for (const auto &el : arr)
{
std::size_t j = 0;
for (const auto &num : el)
this->_data[i][j++] = num;
++i;
}
}
};
int main()
{
double arr[3] = { 1, 2, 3 };
double arr2[2][2] = { {1, 2}, {3, 4} };
test<double, 1, 3> obj = arr;
test<double, 2, 2> obj2 = arr2;
for (const auto &i : obj2)
{
for (const auto &j : i)
std::cout << j << " ";
std::cout << std::endl;
}
std::cin.get();
}
Note: I've been reading about enable_if, but I don't quite understand how it works. Can it be done with that?
The constructors should not be the same, but you have only provided the most generic matching possible.
SFINAE is not necessary here. Just provide a constructor for a 1D array, and a separate constructor for a 2D array:
template <typename T2, std::size_t N>
test( const T2 (&a)[N] )
{
...
}
template <typename T2, std::size_t M, std::size_t N>
test( const T2 (&a)[M][N] )
{
...
}
Another note: POSIX reserves typenames ending with "_t", so it is typically a good idea to avoid them in your own code. (Obnoxious, I know.) Standard C++ will use Camel Case of the form: RowsType, etc, and then typedef a rows_type for users of the class.
Notice, however, that rows_t is not actually a type -- it is a value. A better name would be something like NRows.
Hope this helps.
First, you have to "teach" the compiler what's 2D and what's not. Hence, you have to define something like the following type trait:
template<typename T>
struct is2D : public std::false_type {};
template<typename T, std::size_t N, std::size_t M>
struct is2D<std::array<std::array<T, M>, N>> : std::true_type {};
template<typename T>
struct is2D<std::vector<std::vector<T>>> : std::true_type {};
template<typename T, std::size_t N, std::size_t M>
struct is2D<T[N][M]> : std::true_type {};
Then you could set up your class definition in the following way:
template<class T, std::size_t rows_t, std::size_t cols_t>
class test{
std::array<std::array<T, cols_t>, rows_t> _data;
template<class type_t>
std::enable_if_t<!is2D<type_t>::value, void>
test_init(type_t const &arr) {
std::size_t j = 0;
for (const auto &num : arr) _data[0][j++] = num;
}
template<class type_t>
std::enable_if_t<is2D<type_t>::value, void>
test_init(type_t const &arr) {
std::size_t i = 0;
for(const auto &el : arr) {
std::size_t j = 0;
for (const auto &num : el) _data[i][j++] = num;
++i;
}
}
public:
auto &operator[](const std::size_t &i) { return this->_data[i]; }
auto begin() { return this->_data.begin(); }
auto end() { return this->_data.end(); }
//CONSTRUCTOR
template<class type_t> test(type_t const &arr) { test_init(arr); }
};
LIVE DEMO

How to get the position of a tuple element

For example, I have a tuple
std::tuple<int, int, int, int> a(2, 3, 1, 4);
and I want to get the position of its elements using such as the the following function.
int GetPosition(const std::tuple<int, int, int, int>& tp, int element);
Here 2's position is 0, 3's position is 1, 1's position is 3 and 4'position is 3. How to implement the function? A silly way is to
int GetPosition(const std::tuple<int, int, int, int>& tp, int element)
{
if (std::get<0>(tp) == element) return 0;
if (std::get<1>(tp) == element) return 1;
if (std::get<2>(tp) == element) return 2;
... // Write as more as an allowed max number of elements
}
Any better ways? Thanks.
UPDATE:
I eventually figured out a way to achieve this in a simpler way that also uses short-circuiting (and therefore performs less comparisons).
Given some machinery:
namespace detail
{
template<int I, int N, typename T, typename... Args>
struct find_index
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<I>(t) == val) ? I :
find_index<I + 1, N, T, Args...>::call(t, std::forward<T>(val));
}
};
template<int N, typename T, typename... Args>
struct find_index<N, N, T, Args...>
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<N>(t) == val) ? N : -1;
}
};
}
The function that clients are going to invoke eventually boils down to this simple trampoline:
template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
return detail::find_index<sizeof...(Args), T, Args...>::
call(t, std::forward<T>(val));
}
Finally, this is how you would use it in your program:
#include <iostream>
int main()
{
std::tuple<int, int, int, int> a(2, 3, 1, 4);
std::cout << find_index(a, 1) << std::endl; // Prints 2
std::cout << find_index(a, 2) << std::endl; // Prints 0
std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}
And here is a live example.
EDIT:
If you want to perform the search backwards, you can replace the above machinery and the trampoline function with the following versions:
#include <tuple>
#include <algorithm>
namespace detail
{
template<int I, typename T, typename... Args>
struct find_index
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<I - 1>(t) == val) ? I - 1 :
find_index<I - 1, T, Args...>::call(t, std::forward<T>(val));
}
};
template<typename T, typename... Args>
struct find_index<0, T, Args...>
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<0>(t) == val) ? 0 : -1;
}
};
}
template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
return detail::find_index<0, sizeof...(Args) - 1, T, Args...>::
call(t, std::forward<T>(val));
}
Here is a live example.
ORIGINAL ANSWER:
This does not really sound like a typical way one would use tuples, but if you really want to do this, then here is a way (works with tuples of any size).
First, some machinery (the well-known indices trick):
template <int... Is>
struct index_list { };
namespace detail
{
template <int MIN, int N, int... Is>
struct range_builder;
template <int MIN, int... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_list<Is...> type;
};
template <int MIN, int N, int... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{ };
}
template<int MIN, int MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;
Then, a couple of overloaded function templates:
#include <tuple>
#include <algorithm>
template<typename T, typename... Args, int... Is>
int find_index(std::tuple<Args...> const& t, T&& val, index_list<Is...>)
{
auto l = {(std::get<Is>(t) == val)...};
auto i = std::find(begin(l), end(l), true);
if (i == end(l)) { return -1; }
else { return i - begin(l); }
}
template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
return find_index(t, std::forward<T>(val),
index_range<0, sizeof...(Args)>());
}
And here is how you would use it:
#include <iostream>
int main()
{
std::tuple<int, int, int, int> a(2, 3, 1, 4);
std::cout << find_index(a, 1) << std::endl; // Prints 2
std::cout << find_index(a, 2) << std::endl; // Prints 0
std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}
And here is a live example.
Slightly shorter than the accepted answer, and searching forwards not backwards (so it finds the first match, not the last match), and using constexpr:
#include <tuple>
template<std::size_t I, typename Tu>
using in_range = std::integral_constant<bool, (I < std::tuple_size<Tu>::value)>;
template<std::size_t I1, typename Tu, typename Tv>
constexpr int chk_index(const Tu& t, Tv v, std::false_type)
{
return -1;
}
template<std::size_t I1, typename Tu, typename Tv>
constexpr int chk_index(const Tu& t, Tv v, std::true_type)
{
return std::get<I1>(t) == v ? I1 : chk_index<I1+1>(t, v, in_range<I1+1, Tu>());
}
template<typename Tu, typename Tv>
constexpr int GetPosition(const Tu& t, Tv v)
{
return chk_index<0>(t, v, in_range<0, Tu>());
}
Modified based on comments. A simple way by modifying the canceled answer
template<class Tuple>
struct TupleHelper
{
TupleHelper(Tuple& _tp) : tp(_tp) {}
Tuple& tp;
template<int N>
int GetPosition(int element)
{
if (std::get<N>(tp) == element) return N;
return GetPosition<N+1>(element);
}
template<>
int GetPosition<std::tuple_size<Tuple>::value>(int element)
{
return -1;
}
};
use it as
TupleHelper<MyTupleTy>(myTuple).GetPosition<0>(element);
This seems work.