Reputation: 567
Using C++11 or C++14:
I have a:
constexpr double [2][3][4] = some value;
I want constexpr int [2][3][4]
from this.
(Actually I will want constexpr my_type [2][3][4]
, but that should be trivially solvable once int works.)
The only solution I came up with was a container class with operator[]
overloaded so it behaves like an array. Is there a way to keep standard array syntax? Or possibly std::array<std::array...>>
without creating a new class?
I do not want the input to be std::array
- that is a constraint of the system. The input must be C compatible. The converted array can be C++.
Upvotes: 2
Views: 117
Reputation: 66230
If you accept a constexpr std::array<std::array<int, 4U>, 3U>, 2U>
, well... using SFINAE tag-dispatching, std::rank
, decltype()
, auto
as return type and std::index_sequence
(so a C++14 solution) you can ping-pong play with a getArray()
and his helper getArrayH()
functions as follows
NOTE: answer modified (improved, IMHO) taking inspiration from a Julius's example (thanks!)
#include <array>
#include <iostream>
#include <type_traits>
template <typename T>
std::integral_constant<bool, std::rank<T>::value != 0U> isArray ();
template <typename targetT, typename origT, std::size_t Dim>
constexpr auto getArray (origT(&)[Dim]);
template <typename targetT, typename arrT, std::size_t ... Is>
constexpr auto getArrayH (arrT const & arr,
std::false_type const &,
std::index_sequence<Is...> const &)
{ return std::array<targetT, sizeof...(Is)>{ { targetT(arr[Is])... } }; }
template <typename targetT, typename arrT, std::size_t ... Is>
constexpr auto getArrayH (arrT const & arr,
std::true_type const &,
std::index_sequence<Is...> const &)
{ return std::array<decltype(getArray<targetT>(arr[0])), sizeof...(Is)>
{ { getArray<targetT>(arr[Is])... } }; }
template <typename targetT, typename origT, std::size_t Dim>
constexpr auto getArray (origT(&arr)[Dim])
{ return getArrayH<targetT>(arr, decltype(isArray<origT>()){},
std::make_index_sequence<Dim>{}); }
int main ()
{
constexpr double ad3[2][3][4]
{ { { 1.0, 2.0, 3.0, 4.0 },
{ 2.1, 3.1, 4.1, 5.1 },
{ 3.2, 4.2, 5.2, 6.2 } },
{ { 6.3, 5.3, 4.3, 3.3 },
{ 5.4, 4.4, 3.4, 2.4 },
{ 4.5, 3.5, 2.5, 1.5 } } };
for ( auto const & ad2 : ad3 )
for ( auto const & ad1 : ad2 )
for ( auto const & ad0 : ad1 )
std::cout << ad0 << ' ';
std::cout << std::endl;
constexpr auto ai3 = getArray<int>(ad3);
static_assert(std::is_same<decltype(ai3),
std::array<std::array<std::array<int, 4U>, 3U>, 2U> const>{}, "!");
for ( auto const & ai2 : ai3 )
for ( auto const & ai1 : ai2 )
for ( auto const & ai0 : ai1 )
std::cout << ai0 << ' ';
std::cout << std::endl;
}
Upvotes: 2
Reputation: 1861
Unfortunately, std::array::operator[]
is not constexpr before C++17. Hence you probably need to come up with your own boilerplate.
Here is an example in C++17. If you export the "indices trick" into a separate function (instead of lambda) and implement your own version of std::array
, then this approach should also work with C++14.
#include <array>
#include <iostream>
#include <utility>
////////////////////////////////////////////////////////////////////////////////
template<size_t... is, class F>
constexpr decltype(auto) indexer(std::index_sequence<is...>, F f) {
return f(std::integral_constant<std::size_t, is>{}...);
}
template<size_t N_, class F>
constexpr decltype(auto) indexer(F f) {
constexpr size_t max_index_length = 4096;
constexpr size_t N = std::min(N_, max_index_length);
static_assert(N == N_, "");
return indexer(std::make_index_sequence<N>{}, f);
}
////////////////////////////////////////////////////////////////////////////////
template<class T>
constexpr auto make_array(T val) {
return val;
}
template<class NestedArrays, std::size_t N>
constexpr auto make_array(NestedArrays(&arr)[N]) {
using NestedStdArrays = decltype(make_array(arr[0]));
return indexer<N>([=] (auto... ns) {
return std::array<NestedStdArrays, N>{
make_array(arr[ns])...
};
});
}
////////////////////////////////////////////////////////////////////////////////
int main() {
constexpr int input_from_c[1][3][2]{
{ {0, 10}, {20, 30}, {40, 50} }
};
constexpr auto nested_std_arrays = make_array(input_from_c);
using NestedStdArrays = std::decay_t<decltype(nested_std_arrays)>;
static_assert(
std::is_same<
NestedStdArrays,
std::array<std::array<std::array<int, 2>, 3>, 1>
>{}, ""
);
static_assert(0 == nested_std_arrays[0][0][0], "");
static_assert(10 == nested_std_arrays[0][0][1], "");
static_assert(20 == nested_std_arrays[0][1][0], "");
static_assert(30 == nested_std_arrays[0][1][1], "");
static_assert(40 == nested_std_arrays[0][2][0], "");
static_assert(50 == nested_std_arrays[0][2][1], "");
return 0;
}
Upvotes: 0
Reputation: 42929
There's a way to do this by building incrementally. That is, first solve the conversion for 1D arrays. Then based on that solution the conversion for 2D arrays etc.
template<typename U, typename T, std::size_t N, std::size_t... I>
constexpr
std::array<U, N>
convert1DArrayImpl(std::array<T, N> const &a, std::index_sequence<I...>) {
return {static_cast<U>(a[I])...};
}
template<typename U, typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr
std::array<U, N>
convert1DArray(const std::array<T, N>& a) {
return convert1DArrayImpl<U>(a, Indices{});
}
template<typename U, typename T, std::size_t N, std::size_t M, std::size_t... IN>
constexpr
std::array<std::array<U, N>, M>
convert2DArrayImpl(std::array<std::array<T, N>, M> a, std::index_sequence<IN...>) {
return {convert1DArray<U>(a[IN])...};
}
template<typename U, typename T, std::size_t N, std::size_t M, typename IndicesM = std::make_index_sequence<M>>
constexpr
std::array<std::array<U, N>, M>
convert2DArray(const std::array<std::array<T, N>, M>& a) {
return convert2DArrayImpl<U>(a, IndicesM{});
}
template<typename U, typename T, std::size_t N, std::size_t M, std::size_t K, std::size_t... IN>
constexpr
std::array<std::array<std::array<U, N>, M>, K>
convert3DArrayImpl(std::array<std::array<std::array<T, N>, M>, K> a, std::index_sequence<IN...>) {
return {convert2DArray<U>(a[IN])...};
}
template<typename U, typename T, std::size_t N, std::size_t M, std::size_t K, typename IndicesK = std::make_index_sequence<K>>
constexpr
std::array<std::array<std::array<U, N>, M>, K>
convert3DArray(const std::array<std::array<std::array<T, N>, M>, K>& a) {
return convert3DArrayImpl<U>(a, IndicesK{});
}
Upvotes: 2