'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