Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: Compile Time Calculation of Array

Suppose I have some constexpr function f:

constexpr int f(int x) { ... } 

And I have some const int N known at compile time:

Either

#define N ...; 

or

const int N = ...; 

as needed by your answer.

I want to have an int array X:

int X[N] = { f(0), f(1), f(2), ..., f(N-1) } 

such that the function is evaluated at compile time, and the entries in X are calculated by the compiler and the results are placed in the static area of my application image exactly as if I had used integer literals in my X initializer list.

Is there some way I can write this? (For example with templates or macros and so on)

Best I have: (Thanks to Flexo)

#include <iostream> #include <array> using namespace std;  constexpr int N = 10; constexpr int f(int x) { return x*2; }  typedef array<int, N> A;  template<int... i> constexpr A fs() { return A{{ f(i)... }}; }  template<int...> struct S;  template<int... i> struct S<0,i...> { static constexpr A gs() { return fs<0,i...>(); } };  template<int i, int... j> struct S<i,j...> { static constexpr A gs() { return S<i-1,i,j...>::gs(); } };  constexpr auto X = S<N-1>::gs();  int main() {         cout << X[3] << endl; } 
like image 912
Andrew Tomazos Avatar asked Aug 24 '12 11:08

Andrew Tomazos


2 Answers

There is a pure C++11 (no boost, no macros too) solution to this problem. Using the same trick as this answer we can build a sequence of numbers and unpack them to call f to construct a std::array:

#include <array> #include <algorithm> #include <iterator> #include <iostream>  template<int ...> struct seq { };  template<int N, int ...S> struct gens : gens<N-1, N-1, S...> { };  template<int ...S> struct gens<0, S...> {   typedef seq<S...> type; };  constexpr int f(int n) {   return n; }  template <int N> class array_thinger {   typedef typename gens<N>::type list;    template <int ...S>   static constexpr std::array<int,N> make_arr(seq<S...>) {     return std::array<int,N>{{f(S)...}};   } public:   static constexpr std::array<int,N> arr = make_arr(list());  };  template <int N> constexpr std::array<int,N> array_thinger<N>::arr;  int main() {   std::copy(begin(array_thinger<10>::arr), end(array_thinger<10>::arr),              std::ostream_iterator<int>(std::cout, "\n")); } 

(Tested with g++ 4.7)

You could skip std::array entirely with a bit more work, but I think in this instance it's cleaner and simpler to just use std::array.

You can also do this recursively:

#include <array> #include <functional> #include <algorithm> #include <iterator> #include <iostream>  constexpr int f(int n) {   return n; }  template <int N, int ...Vals> constexpr typename std::enable_if<N==sizeof...(Vals),std::array<int, N>>::type make() {   return std::array<int,N>{{Vals...}}; }  template <int N, int ...Vals> constexpr typename std::enable_if<N!=sizeof...(Vals), std::array<int,N>>::type  make() {   return make<N, Vals..., f(sizeof...(Vals))>();   }  int main() {   const auto arr = make<10>();   std::copy(begin(arr), end(arr), std::ostream_iterator<int>(std::cout, "\n")); } 

Which is arguably simpler.

like image 158
Flexo Avatar answered Sep 18 '22 19:09

Flexo


Boost.Preprocessor can help you. The restriction, however, is that you have to use integral literal such as 10 instead of N (even be it compile-time constant):

#include <iostream>  #include <boost/preprocessor/repetition/enum.hpp>  #define VALUE(z, n, text) f(n)  //ideone doesn't support Boost for C++11, so it is C++03 example,  //so can't use constexpr in the function below int f(int x) { return x * 10; }  int main() {   int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) };  //N = 10   std::size_t const n = sizeof(a)/sizeof(int);   std::cout << "count = " << n << "\n";   for(std::size_t i = 0 ; i != n ; ++i )      std::cout << a[i] << "\n";   return 0; } 

Output (ideone):

count = 10 0 10 20 30 40 50 60 70 80 90 

The macro in the following line:

int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) };  

expands to this:

int const a[] = {f(0), f(1), ... f(9)};  

A more detail explanation is here:

  • BOOST_PP_ENUM
like image 20
Nawaz Avatar answered Sep 18 '22 19:09

Nawaz