One way to implement a C++11 array that has its elements initialized by a function of their index calculated by the compiler and have the results stored in the data section (.rodata) of the application image is to use templates, partial specialization and constexpr as follows:
#include <iostream>
#include <array>
using namespace std;
constexpr int N = 1000000;
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;
}
This doesn't work for large values of N:
error: constexpr evaluation depth exceeds maximum of 512
This is because of the head-tail style of recursive template evaluation, that has linear depth in terms of N.
Is there a way to do it such that the evaluation depth is logarithmic in terms of N, rather than linear? (and hence would avoid the default depth limit)
Some programming languages allow calculation of values at compile time. Calculate 10! (ten factorial) at compile time. Print the result when the program is run. Discuss what limitations apply to compile-time calculations in your language. First example with the assembler equivalence pseudo instruction (EQU):
Coding a Compile-Time Array A compile-time array is specified using the essential array specifications plus the keyword CTDATA. In addition, on a definition specification you can specify:
Compile-time calculations in C++ look quite different from normal code. We can only use templates, type definitions and a subset of integer arithmetic. It is not possible to use iteration. C++ compile-time programs are similar to programs in pure functional programming languages, albeit with a peculiar syntax.
Explain Compile time and Run time initialization in C programming? Let’s take the concept of arrays to about compile time and run time initialization − Array is a collection of items stored at contiguous memory locations and elements can access by using indices. In compile time initialization, user has to enter the details in the program itself.
In c++14 with general constexpression function needn't any sequence, make_sequence
static constexpr int f(int i) noexcept { return i * 2; }
template< int N, typename F >
static constexpr std::array<int,N> generate_array( F fo ) noexcept
{
std::array< int, N > result{}; // init with zero
int i = 0;
for( auto& x : result) x = fo(++i);
return result;
}
// test
constexpr auto arr = generate_array<10>( f );
Only has one problem, there is no compiler which can compile it :)
If what you're using in the code is a weird form of the indices trick, here's an implementation that has O(log N)
instantiations:
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<unsigned...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;
template<unsigned N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
// example
template<unsigned... Is>
void f(seq<Is...>);
int main(){
f(gen_seq<6>());
}
Live example.
The only tail recursive template, that's causing the linear template instantiation depth, is the construction of the list of integers in the template parameter list of S
.
This can be done in logarithmic instantiation depth, as you suggest:
template <int ... Ints> struct seq;
template <int Start, int End>
struct range
{
typedef concat_seq<range<Start, Start+(End-Start)/2>::type, range<Start+(End-Start)/2, End>::type>::type type;
};
template<int Singleton>
struct range<Singleton, Singleton+1>
{
typedef seq<Singleton> type;
};
template <int NoSingleton>
struct range<NoSinleton, NoSingleton>
{
typedef seq<> type;
};
Add a bunch of typename
s where appropriate, implement concat_seq
(easy), extract the argument list for fs
from range<0, N>::type
and there you go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With