2

Suppose for the sake of argument I have following private constexpr in a class:

static constexpr uint16_t square_it(uint16_t x)
{
    return std::pow(x, 2);
}

Then I want to construct a static constant array of these values for the integers up to 255 in the same section of the same class using the above constexpr:

static const uint16_t array_of_squares[256] =
{
    //something
};

I'd like the array to be constructed at compile time, not at runtime if possible. I think the first problem is that using expressions like std::pow in a constexpr is not valid ISO C++ (though allowed by arm-gcc maybe?), as it can return a domain error. The actual expression I want to use is a somewhat complicated function involving std::exp.

Note that I don't have much of the std library available as I'm compiling for a small microprocessor, the Cortex M4.

Is there a more appropriate way to do this, say using preprocessor macros? I'd very much like to avoid using something like an external Python script to calculate the table each time it needs to be modified during development, and then pasting it in.

MattyZ
  • 1,541
  • 4
  • 24
  • 41
  • 2
    Have an external Python script calculate the table, and write out a C++ file that can be used directly in the build; no copy/pasting required. – Igor Tandetnik Mar 20 '16 at 18:45

2 Answers2

1

The problem, as you say, is that C standard library functions are in general not marked constexpr.

The best workaround here, if you need to use std::exp, is to write your own implementation that can be run at compile-time. If it's meant to be done at compile-time, then optimizing it probably isn't necessary, it only needs to be accurate and moderately efficient.

Someone asked a question about how to do that here a long time ago. You could reuse the idea from there and rewrite as a constexpr function in C++11, although you'd have to refactor it to avoid the for loop. In C++14 less refactoring would be required.

You could also try doing it strictly via templates, but it would be more painful, and double cannot be a template parameter so it would be more complicated.

Community
  • 1
  • 1
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
1

How about something like this?

constexpr uint16_t square_it(uint16_t v) { return v*v; }

template <size_t N, class = std::make_index_sequence<N>>
struct gen_table;

template <size_t N, size_t... Is>
struct gen_table<N, std::index_sequence<Is...>> {
    static const uint16_t values[N] = {square_it(Is)...};
};

constexpr auto&& array_of_squares = gen_table<256>::values;

I have no idea whether that microprocessor supports this sort of operation. It may not have make_index_sequence in your standard library (though you can find implementations on SO), and maybe that template instantiation will take too much memory. But at least it's something that works somewhere.

Barry
  • 286,269
  • 29
  • 621
  • 977