C++11 Constexpr Flatten List of Std::Array into Array

How to generate an inline constexpr plain array of array in c++17

Just use std::array<std::span<int>, N>, which is a fixed size array of spans of different sizes. To generate this, use an std::index_sequence

Header:

constexpr std::size_t size_of_A = 500;
extern const std::array<const std::span<const int>, size_of_A>& A;

Implementation:

constexpr std::size_t size_of_B_in_A(std::size_t i) { return i%10+1;}
constexpr int f(std::size_t i, std::size_t j) {return static_cast<int>(i%(j+1));}

template <int I, int N>
struct B
{
std::array<int,N> arr;
explicit constexpr B()
{
for (int j = 0; j < N; j++)
arr[j] = f(I, j);
}
constexpr operator const std::span<const int>() const {return {arr};}
};

template<class index_sequence>
class BGen;
template<std::size_t... I>
struct BGen<std::integer_sequence<std::size_t,I...>> {
static constexpr std::tuple<B<I, size_of_B_in_A(I)>...> bs{};
static constexpr std::array<const std::span<const int>, sizeof...(I)> A {std::get<I>(bs)...};
};
const std::array<const std::span<const int>, size_of_A>& A
= BGen<decltype(std::make_index_sequence<size_of_A>{})>::A;

Usage:

int main()
{
for (unsigned i = 0; i < A.size() ; i++)
{
for (unsigned j = 0; j < A[i].size(); j++)
{
std::cout << A[i][j];
}
}
}

http://coliru.stacked-crooked.com/a/d68b0e9fd6142f86


However, stepping back: This solution is NOT the normal way to go about solving this problem. Since it's all constexpr, this is all data not code. Ergo, the most performant solution is two programs. One generates the data and saves it to a file that ships with (inside?) your program. Then your program simply maps the file into memory, and uses the data directly.

Initialize a constexpr array as sum of other two constexpr arrays

Similar things have been done many times already, but here's a solution for this particular compile-time operation on arrays ;)

template<int... Is>
struct seq {};
template<int I, int... Is>
struct gen_seq : gen_seq<I-1, I-1, Is...> {};
template<int... Is>
struct gen_seq<0, Is...> : seq<Is...> {};

#include <array>

template<class T, int N, int... Is>
constexpr std::array<T, N> sum(T const (&lhs)[N], T const (&rhs)[N], seq<Is...>)
{
return {{lhs[Is]+rhs[Is]...}};
}

template<class T, int N>
constexpr auto sum(T const (&lhs)[N], T const (&rhs)[N])
-> decltype( sum(lhs, rhs, gen_seq<N>{}) )
{
return sum(lhs, rhs, gen_seq<N>{});
}

#include <iostream>
int main()
{
constexpr int a[] = {1,2,3,4,5};
constexpr int b[] = {1,2,3,4,5};
constexpr auto c = sum(a,b);

for(auto e : c) std::cout << e << ", ";
}

N.B. std::array::operator[] is not constexpr in C++11, therefore I've used raw arrays as input.


For arbitrary binary functions:

template<class T, int N, class F, int... Is>
constexpr auto transform(T const (&lhs)[N], T const (&rhs)[N], F f,
seq<Is...>)
-> std::array<decltype( f(lhs[0], rhs[0]) ), N>
{
return {{ f(lhs[Is], rhs[Is])... }};
}

template<class T, int N, class F>
constexpr auto transform(T const (&lhs)[N], T const (&rhs)[N], F f)
-> decltype( transform(lhs, rhs, f, gen_seq<N>{}) )
{
return transform(lhs, rhs, f, gen_seq<N>{});
}

constexpr int sum(int l, int r) { return l+r; }
// ...
constexpr auto c = transform(a,b,sum);

For arbitrary n-ary functions and arbitrary array-like types:

template<class F, class... Args>
constexpr auto index_invoke(F f, int i, Args&&... args)
-> decltype( f(args[i]...) )
{
return f(args[i]...);
}

template<class F, int... Is, class... Args>
constexpr auto transform_impl(F f, seq<Is...>, Args&&... args)
-> std::array<decltype( f(args[0]...) ), sizeof...(Is)>
{
return {{ index_invoke(f, Is, std::forward<Args>(args)...)... }};
}

template <class T, class...>
struct get_extent_helper
: std::integral_constant<int,
std::extent<typename std::remove_reference<T>::type>{}>
{};

template<class F, class... Args>
constexpr auto transform(F f, Args&&... args)
-> decltype( transform_impl(f, gen_seq< get_extent_helper<Args...>{} >{},
std::forward<Args>(args)...) )
{
using N = get_extent_helper<Args...>;
return transform_impl(f, gen_seq<N{}>{}, std::forward<Args>(args)...);
}

More lightweight with an alias-template:

template <class T, class...>
using get_extent_helper =
std::integral_constant<int,
std::extent<typename std::remove_reference<T>::type>{}>;

But this is buggy under g++4.8.1

How to filter a constexpr `std::array` with a `UnaryPredicate` lambda?

it's definitely that, dependent type, the type that depends on a value, is impossible in present-day C++. you can't decide the size M of result type std::array<T, M> by only the function arguments, which means M must be a template argument.

so you have to provide an additional template argument M for the result type, by a constexpr function count_if, and then just decide each element to be which value:

template<size_t I, typename T, typename Pred, std::enable_if_t<I == 0, int> = 0>
constexpr T get_element_helper(T const* arr, Pred&& pred){
return pred(arr[0]) ? arr[0] : get_element_helper<0>(arr + 1, pred);
}
template<size_t I, typename T, typename Pred, std::enable_if_t<I != 0, int> = 0>
constexpr T get_element_helper(T const* arr, Pred&& pred){
return pred(arr[0]) ? get_element_helper<I - 1>(arr + 1, pred) : get_element_helper<I>(arr + 1, pred);
}

template<typename T, size_t N, typename Pred, size_t ...Is>
constexpr std::array<T, sizeof...(Is)> filter_array_helper(std::array<T, N> const& arr, Pred&& pred, std::index_sequence<Is...>*){
return { get_element_helper<Is>(arr.data(), pred)... };
}
template<size_t M, typename T, size_t N, typename Pred>
constexpr std::array<T, M> filter_array(std::array<T, N> const& arr, Pred&& pred){
return filter_array_helper(arr, std::forward<Pred>(pred), (std::make_index_sequence<M>*)nullptr);
}

template<typename T, size_t N, typename Pred, size_t... Is>
constexpr size_t count_if_helper(std::array<T, N> const& arr, Pred&& pred, std::index_sequence<Is...>*){
return ((size_t)(bool)pred(arr[Is]) + ...);
}
template<typename T, size_t N, typename Pred>
constexpr size_t count_if(std::array<T, N> const& arr, Pred&& pred){
return count_if_helper(arr, std::forward<Pred>(pred), (std::make_index_sequence<N>*)nullptr);
};

int main(){
constexpr std::array<int, 5> a = { 0, 1, 2, 3, 4 };
constexpr auto pred = [](int a){ return a % 2 == 0; };
constexpr auto b = filter_array<count_if(a, pred)>(a, pred); // std::array<int, 3>{0, 2, 4}
}

how to define an array of array in c++

You can use std::array to get around the problem.

#include <array>

using myType = std::array<uint8_t, 16>;

int main()
{
constexpr myType x0={1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};
constexpr myType x1={11,12,13,14,15,16,17,18,19,10,11,12,13,14,15,16};
constexpr myType x2={21,22,23,24,25,26,27,28,29,20,21,22,23,24,25,26};

constexpr std::array<myType, 3> AllX = {x0,x1,x2};
}

In a comment, you said,

But I can not use this method as x0 and x1 and x2 and .. is already defined in that way in code and I can not change it.

In that case, your only option is to copy the elements of those objects to AllX. You can use std::copy to streamline that. The only problem is that you can't use constexpr AllX.

   std::array<myType, 3> AllX = {};
std::copy(begin(x0), end(x0), AllX[0].data());
std::copy(begin(x1), end(x1), AllX[1].data());
std::copy(begin(x2), end(x2), AllX[2].data());

how to convert C-style compile-time arrays to std::array

You have a choice. Either use template argument deduction, or specify the arguments yourself.

Using template argument deduction:

static constexpr std::array g_numbers{1, 2, 3, 4, 5};

struct g_collection_element
{
int m_nID;
char const* m_pszName;
};

static constexpr std::array g_collection{
g_collection_element{ 1, "Max" },
g_collection_element{ 2, "Fabian" },
g_collection_element{ 3, "Martin" },
};

As you can see here, I named the struct and used it after declaration. I also removed the const from the members as they are not really useful in that case.

Also, for template argument deduction to work, you must type out the type of the elements for each initializer in the array, or the compiler won't perform template argument deduction.

Specifying the arguments:

static constexpr std::array<int, 5> g_numbers{1, 2, 3, 4, 5};

struct g_collection_element
{
int m_nID;
char const* m_pszName;
};

static constexpr std::array<g_collection_element, 3> g_collection{
{ 1, "Max" },
{ 2, "Fabian" },
{ 3, "Martin" },
};

You sadly can't only specify the type to let the number to be deduced.

How to flatten heterogeneous lists (aka tuples of tuples of ...)

I propose to SFINAE on presence of tuple

// Simple traits
template <typename T> struct is_tuple : std::false_type{};
template <typename... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type{};

// utility to ensure return type is a tuple
template<typename T>
constexpr decltype(auto) as_tuple(T t) { return std::make_tuple(t); }

template<typename ...Ts>
constexpr decltype(auto) as_tuple(std::tuple<Ts...> t) { return t; }

// Simple case
template<typename T>
constexpr decltype(auto) flatten(T t)
{
return t;
}

// Possibly recursive tuple
template<typename T>
constexpr decltype(auto) flatten(std::tuple<T> t)
{
return flatten(std::get<0>(t));
}

// No more recursion, (sizeof...Ts != 1) with above overload
template<typename ...Ts, std::enable_if_t<!(is_tuple<Ts>::value || ...), bool> = false>
constexpr decltype(auto) flatten(std::tuple<Ts...> t)
{
return t;
}

// Handle recursion
template<typename ...Ts, std::enable_if_t<(is_tuple<Ts>::value || ...), bool> = false>
constexpr decltype(auto) flatten(std::tuple<Ts...> t)
{
return std::apply([](auto...ts)
{
return flatten(std::tuple_cat(as_tuple(flatten(ts))...));
}, t);
}

Demo



Related Topics



Leave a reply



Submit