'Is there way to construct from initializer_list in compile time?
I'm writing C++ ndarray class. I need both dynamic-sized and compile-time-size-known arrays (free store allocated and stack allocated, respectively).
I want to support initializing from nested std::initializer_list.
Dynamic-sized one is OK: This works perfectly.
frozenca::Array<float, 2> arr {{1, 2, 3}, {4, 5, 6}};
std::cout << arr.size() << '\n'; // 6
std::cout << arr[{1, 1}] << '\n'; // 5
Static-sized one is also OK: This works fine.
frozenca::StaticArray<float, 2, 3> arr2 {{1, 2, 3}, {4, 5, 6}};
std::cout << arr2.size() << '\n'; // 6
std::cout << arr2[{1, 1}] << '\n'; // 5
// static_assert(arr2[{1, 1}] == 5); // THIS DOES NOT WORK
But I hate that the initialization is done in runtime actually. I want to make it as compile-time operation. My implementation detail is as follows:
template <std::semiregular T, std::size_t N>
using DenseInitializer_t = typename DenseInitializer<T, N>::type;
template <std::semiregular T, std::size_t N>
struct DenseInitializer {
using type = std::initializer_list<DenseInitializer_t<T, N - 1>>;
};
template <std::semiregular T>
struct DenseInitializer<T, 1> {
using type = std::initializer_list<T>;
};
template <std::semiregular T>
struct DenseInitializer<T, 0>;
template <std::semiregular Scalar, int... Sizes>
template <typename Initializer>
constexpr void StaticArray<Scalar, Sizes...>::verifyDims(const Initializer& init) const {
checkDims<0, Initializer, Sizes...>(init);
}
template <std::semiregular Scalar, int... Sizes>
StaticArray<Scalar, Sizes...>::StaticArray(DenseInitializer_t<value_type, N> init) {
verifyDims(init);
insertFlat(data(), init);
}
template <typename Initializer>
constexpr bool checkNonJagged(const Initializer& init) {
auto i = std::cbegin(init);
return std::all_of(init.begin(), init.end(), [&i](const auto& it) {
return it.size() == i->size();
});
}
template <int k, typename Initializer, int... Sizes>
void checkDims(const Initializer& init) {
if constexpr (k < sizeof...(Sizes) - 1) {
if (!checkNonJagged(init)) {
throw std::invalid_argument("Jagged matrix initializer");
}
}
if (std::get<k>(std::forward_as_tuple(Sizes...)) != std::ssize(init)) {
throw std::invalid_argument("Matrix initializer does not match with static matrix");
}
if constexpr (k < sizeof...(Sizes) - 1) {
checkDims<k + 1, decltype(*std::begin(init)), Sizes...>(*std::begin(init));
}
}
template <std::semiregular T>
void addList(T* data,
const T* first, const T* last,
int& index) {
for (; first != last; ++first) {
data[index] = *first;
++index;
}
}
template <std::semiregular T, typename I>
void addList(T* data,
const std::initializer_list<I>* first, const std::initializer_list<I>* last,
int& index) {
for (; first != last; ++first) {
addList(data, first->begin(), first->end(), index);
}
}
template <std::semiregular T, typename I>
void insertFlat(T* data, std::initializer_list<I> list) {
int index = 0;
addList(data, std::begin(list), std::end(list), index);
}
addList and checkNonJagged doesn't work as compile-time, because the compiler thinks that init is not known as compile time even within the example case.
How can I make this as compile-time operation?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
