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.

8
  • 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++. Once I fix that and switch to std::array, simply slapping constexpr onto it makes it work at compile-time: gcc.godbolt.org/z/dTe7srjTY Commented Dec 28, 2022 at 6:38
  • My original code works, see (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. Commented Dec 28, 2022 at 7:04
  • I mean the first snippet doesn't work. Sorry, my bad, I needed more constexpr (on the resulting variable and all functions): gcc.godbolt.org/z/W67P1zbEd Commented Dec 28, 2022 at 7:23
  • only clang produces table of precalculated values. ideally, if constexpr are right but not used , you got zero code at all. e.g. 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) Commented Dec 28, 2022 at 7: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. Commented Dec 28, 2022 at 7:42

2 Answers 2

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);
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, that answers my questions and works just fine.
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 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 :)
0

With the help of a small generic function (starting with c++20)

template<std::size_t N, typename FCT, typename...ARGS>
constexpr auto sequence (FCT fct, ARGS...args)
{
    return [&] <std::size_t...Is> (std::index_sequence<Is...>) {
        return fct.template operator() <Is...>  ();
    } (std::make_index_sequence<N>{});
}

you can simply write

template <int size, class Func> 
constexpr auto fillArray(Func func) 
{
    return sequence<size> ([func] <std::size_t...i>  { return std::array { func(i)... }; });
}

constexpr static auto CALC_FIRST_10_ADD2 = fillArray<10>(calcAdd2);
constexpr static auto CALC_FIRST_10_POW2 = fillArray<10>([](float a) { return a*a; });

Demo

Note that it is possible to reuse sequence in other use cases.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.