9

C++ noob here. What's the simplest way to create the array {f(0), f(1), ..., f(1023)} at compile time, given a constexpr f?

Markus O
  • 113
  • 1
  • 4
  • 1
    Not sure which compiler / C++ standard you're one but [this](http://coliru.stacked-crooked.com/a/bbe7f523104abb94) seems to do the trick? – Borgleader May 19 '19 at 11:11

3 Answers3

8

You can use an immediately invoked lambda:

#include <array>

using ResultT = int;
constexpr ResultT f(int i)
{
    return i * 2;
}

constexpr auto LUT = []
{
    constexpr auto LUT_Size = 1024;
    std::array<ResultT, LUT_Size> arr = {};

    for (int i = 0; i < LUT_Size; ++i)
    {
        arr[i] = f(i);
    }

    return arr;
}();

static_assert(LUT[100] == 200);
DeviatioN
  • 340
  • 1
  • 3
  • 8
  • I've always been surprised that `` doesn't have a function template along the lines of `std::generate_n` that passes the index to the generator. Generators shouldn't have to carry their own state. – Adrian McCarthy Jul 02 '20 at 14:25
5

DeviationN's solution requires C++17 (for constexpr lambda).

As a complement here is a solution working with C++14

#include <array>

constexpr int f(int i) { return 2 * i; }

template <std::size_t... I>
constexpr auto lookup_helper(std::index_sequence<I...>)
{
  return std::array<int, sizeof...(I)>({f(I)...});
}

template <size_t N>
constexpr auto lookup()
{
  return lookup_helper(std::make_index_sequence<N>());
}

int main()
{
  constexpr int N = 10;
  constexpr auto a = lookup<N>();

  // Check it works
  static_assert(a[N-1]==2*(N-1));
}

The idea is to use std::index_sequence<I...>. However if you can use c++17 DeviationN's solution is more convenient.

Picaud Vincent
  • 10,518
  • 5
  • 31
  • 70
5

The lambda solution is fine for a one-off. If you have to make a few, you can generalize it with a constexpr function template like this:

#include <algorithm>
#include <array>
#include <cstdint>

template <typename T, std::size_t N, typename Generator>
constexpr std::array<T, N> make_array(Generator fn) {
    std::array<T, N> table = {};
    for (std::size_t i = 0; i != N; ++i) {
        table[i] = fn(i);
    }
    return table;
}

Then, you can use it to create your compile-time lookup table:

constexpr float doubler(std::size_t i) { return 2.0f * i; }

constexpr auto lookup_table = make_array<float, 5>(doubler);
static_assert(lookup_table[3] == 6.0f);
Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175