1

I struggle to pass any constexpr function as a parameter to the constexpr array computation.

How I would do this in a common C++ (not constexpr, just to show what I want)

float calcAdd2(float a) {
    return a + a;
}
float calcPow2(float a)
{
    return a * a;
}
template <class Func>
auto calcArrArithmetic(int size, Func func) {
    std::vector<float> result;
    for (auto i = 0; i < size; i++) {
        result.emplace_back(func(i));
    }
    return result;
}

However, it's not that simple in constexpr. I started with plain array computation

template <std::size_t... I>
std::array<float, sizeof...(I)> fillArray(std::index_sequence<I...>) {
    return std::array<float, sizeof...(I)>{
        calcAdd2(I)...
    };
}
template <std::size_t N>
std::array<float, N> fillArray() {
    return fillArray(std::make_index_sequence<N>{});
}
static const auto CALC_FIRST_10_ADD2 = fillArray<10>();

Link. It worked. Now how to generalize calcAdd2 to be not only calcAdd2, but any constexpr function I would like to? My goal looks like:

static const auto CALC_FIRST_10_ADD2 = fillArray<10>(calcAdd2);
static const auto CALC_FIRST_10_POW2 = fillArray<10>(calcPow2);

EDIT: C++17 Answer to the question by madhur4127.

EDIT2. C++20 Answer by BlackCatHole.

Vladislav Kogan
  • 561
  • 6
  • 15
  • 3
    Your original code is wrong: a dangling pointer is being returned, and the array size isn't constexpr, which [isn't allowed in standard C++](https://stackoverflow.com/q/1887097/2752075). Once I fix that and switch to `std::array`, simply slapping `constexpr` onto it makes it work at compile-time: https://gcc.godbolt.org/z/dTe7srjTY – HolyBlackCat Dec 28 '22 at 06:38
  • My original code works, see (https://gcc.godbolt.org/z/4vdnrshb4). There are pre-computed values at the bottom there (# float 0, # float 2, etc). Where are compile-time computed values in your link? I can't find any. It seems to me that your approarch compiles perfectly, but the values being evaluated at runtime. – Vladislav Kogan Dec 28 '22 at 07:04
  • I mean [the first snippet](https://gcc.godbolt.org/z/56oK7aqob) doesn't work. Sorry, my bad, I needed more `constexpr` (on the resulting variable and all functions): https://gcc.godbolt.org/z/W67P1zbEd – HolyBlackCat Dec 28 '22 at 07:23
  • only clang produces table of precalculated values. ideally, if constexpr are right but not used , you got zero code at all. e.g. https://gcc.godbolt.org/z/hYeMbMj9T Orignal snippet works only in clang and only if function is used as compile-time, it's a loophole not defined by standard (behaviour of VLA not guaranteed at all) – Swift - Friday Pie Dec 28 '22 at 07:24
  • HolyBlackCat, your link doesn't compiles. Is it what you really meaned? As for first snippet, it had never meant to be constexpr, just common code to show what I want. I've added additional comment to clarify that. Swift Friday Pie, you got zero code because -O2 flag erased it. I'm not making computations to never use them, that doesn't help. – Vladislav Kogan Dec 28 '22 at 07:42
  • :( That's what I get for commenting too fast. It works in C++20, but in C++17 you also need to zero the array: https://gcc.godbolt.org/z/eY8baacdo – HolyBlackCat Dec 28 '22 at 08:15
  • *"first snippet, it had never meant to be constexpr"* The first snippet is outright wrong, constexpr or not. – HolyBlackCat Dec 28 '22 at 08:15
  • Oh, I see why now. My bad. Fixed first snippet. Added your C++20 solution as an answer. Thanks. – Vladislav Kogan Dec 28 '22 at 18:14

1 Answers1

3

Here's the simplified version of your code that does what you want:

constexpr float calcAdd2(float a) { return a + a; }

constexpr float calcPow2(float a) { return a * a; }

template <std::size_t N, typename Func>
constexpr auto fillArray(Func&& func) {
    std::array<float, N> ret{};
    for(unsigned i=0;i<N;++i) {
        ret[i] = func(i);
    }
    return ret;
}

constexpr auto CALC_FIRST_10_ADD2 = fillArray<10>(calcAdd2);
madhur4127
  • 276
  • 2
  • 13
  • 1
    Thank you, that answers my questions and works just fine. – Vladislav Kogan Dec 28 '22 at 07:43
  • Making an array full of default values and then assigning its elements to what you want (what you do) is in general different from making an array that starts with the elements you want right from initialization (OP's code). If the type were generalized from `float` to any `T`, this would matter. But the idea of this answer is right: you "just" thread a functor through. – HTNW Dec 28 '22 at 07:47
  • @HTNW you're right. I used this approach because it is simpler and compile time efficient. IIRC Fold expressions are memory intensive at higher `N`. Use the right tool for the job :) – madhur4127 Dec 28 '22 at 07:55