I have a wrapper struct around a vector and a shape, like this:
template <std::size_t N>
struct array_type {
std::array<std::size_t, N> shape;
std::vector<float> data;
};
I would like to be able to construct an array_type
from any array float[N]
, float[N][M]
, etc.
Currently, I have a function for each rank, e.g.
template <std::size_t N>
array_type<1> _1d(const deel::formal::float_type (&values)[N]) {
return {{N},
std::vector<float_type>(
reinterpret_cast<const float_type*>(values),
reinterpret_cast<const float_type*>(values) + N)};
}
template <std::size_t N>
array_type<2> _2d(const deel::formal::float_type (&values)[N][M]) {
return {{N, M},
std::vector<float_type>(
reinterpret_cast<const float_type*>(values),
reinterpret_cast<const float_type*>(values) + N * M)};
}
I would like to write something like:
template <class Array>
array_type<std::rank_v<Array>> make_array(Array const&);
...but that does not work for initializer list:
auto arr1 = _1d({1, 2, 3}); // Ok
auto arr2 = make_array({1, 2, 3}); // Ko
Is there a way to have this make_array
? Or (since I think it's not possible), to have at least something like make_array<1>({1, 2, 3})
where the rank is explicitly specified?
array_type::shape
can be generated with std::extent
with a bit of template meta programming:
template<typename Array, std::size_t... I>
auto extents_impl(const Array& a, std::index_sequence<I...>)
{
return std::array{std::extent_v<Array, I>...};
}
template<typename Array>
auto extents(const Array& a)
{
return extents_impl(a, std::make_index_sequence<std::rank_v<Array>>());
}
With this, make_array
can be written as:
template <class Array, std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<Array>>, float>, int> = 0> // magic incantation :)
array_type<std::rank_v<Array>> make_array(Array const& a)
{
array_type<std::rank_v<Array>> ret {
.shape = extents(a),
.data = std::vector<float>(sizeof a / sizeof(float)),
};
std::memcpy(ret.data.data(), &a, sizeof a);
return ret;
}
I use memcpy
to avoid potential pointer type aliasing violations and the technicality of iterating outside the bounds of sub array being UB according to strict interpretation of the standard.
...but that does not work for initializer list:
Add one overload:
template <std::size_t N>
array_type<1> make_array(float const (&a)[N])
{
return make_array<float[N]>(a);
}
Alternatively, you can specify the array type in the call:
make_array<float[2][3]>({{1, 2, 3},{4, 5, 6}});
This requires no overloads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With