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
andx1
andx2
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
How to Detect Possible/Potential Stack Overflow Problems in a C/C++ Program
Initialize Std::Array with a Range (Pair of Iterators)
Visual Studio Project Out of Date
What Is the Purpose of C++20 Std::Common_Reference
Virtual Destructor: Is It Required When Not Dynamically Allocated Memory
Copy Constructor in C++ Is Called When Object Is Returned from a Function
Call of Overloaded Function Is Ambiguous
Setting Thread Priority in Linux with Boost
Preventing Non-Const Lvalues from Resolving to Rvalue Reference Instead of Const Lvalue Reference
Should We Still Be Optimizing "In the Small"
How to Pan Images in Qgraphicsview
What Strategies Have You Used to Improve Build Times on Large Projects
C++ Template Instantiation: Avoiding Long Switches
Avoid Exponential Grow of Const References and Rvalue References in Constructor
Warning C4003 and Errors C2589 and C2059 On: X = Std::Numeric_Limits<Int>::Max();
Std::Unordered_Map::Find Using a Type Different Than the Key Type
Amortized Analysis of Std::Vector Insertion
May Volatile Be in User Defined Types to Help Writing Thread-Safe Code