Reputation: 10539
Suppose we have code like this. It works well and pre-calculate first 5 Fibonacci numbers.
#include <iostream>
template <int T>
struct fib;
template <>
struct fib<0>{
constexpr static int value = 1;
};
template <>
struct fib<1>{
constexpr static int value = 1;
};
template <int I>
struct fib{
constexpr static int value = fib<I - 1>::value + fib<I - 2>::value;
};
int main(){
std::cout << fib<0>::value << std::endl;
std::cout << fib<1>::value << std::endl;
std::cout << fib<2>::value << std::endl;
std::cout << fib<3>::value << std::endl;
std::cout << fib<4>::value << std::endl;
std::cout << fib<5>::value << std::endl;
}
However there is "small" problem with it.
What if we need to use this for values, that are not known at compile time?
For few values we can do this:
const int max = 5;
int getData(){
return 5; // return value between 0 and max.
}
int something(){
switch(getData()){
case 0: return fib<0>::value;
case 1: return fib<1>::value;
case 2: return fib<2>::value;
case 3: return fib<3>::value;
case 4: return fib<4>::value;
case 5: return fib<5>::value;
}
}
This will works OK for 5 values, but what if we have 150 or 300?
Is not really serious to change the code with 300 rows...
What could be the workaround here?
Upvotes: 4
Views: 159
Reputation: 66200
If you need to use a value at runtime that isn't known at compile time, you can't compute it at compile time. Obvious.
But... if you can impose a top value to values needed, you can compute all values (from zero to top) at compile time and store them in an std::array
.
In the following example I have modified your fib
structs (to use a std::size_t
index and a template type (with default unsigned long
) for the value) and I have added a templated struct fibVals
that contain an std::array
that is initialized using fib<n>::value
The following main()
show that is possible to define a constexpr fibvals<N>
(with N == 20
in the example) to compute (at compile time) all fib<n>
values in range [0,N[.
#include <array>
#include <utility>
#include <iostream>
template <std::size_t, typename T = unsigned long>
struct fib;
template <typename T>
struct fib<0U, T>
{ constexpr static T value { T(1) }; };
template <typename T>
struct fib<1U, T>
{ constexpr static T value { T(1) }; };
template <std::size_t I, typename T>
struct fib
{ constexpr static T value { fib<I-1U>::value + fib<I-2U>::value }; };
template <std::size_t I, typename T = unsigned long>
struct fibVals
{
const std::array<T, I> vals;
template <std::size_t ... Is>
constexpr fibVals ( std::index_sequence<Is...> const & )
: vals { { fib<Is, T>::value ... } }
{ }
constexpr fibVals () : fibVals { std::make_index_sequence<I> { } }
{ }
};
int main()
{
constexpr fibVals<20> fv;
for ( auto ui = 0U ; ui < fv.vals.size() ; ++ui )
std::cout << "fib(" << ui << ") = " << fv.vals[ui] << std::endl;
}
Unfortunately this example use std::make_index_sequence<I>
and std::index_sequence<Is...>
that are C++14 features.
If you want implement struct fibVals
in C++11, you can implement the following structs struct indexSeq
and struct indexSeqHelper
, to substitute std::index_sequence<Is...>
and std::make_index_sequence<I>
template <std::size_t ...>
struct indexSeq
{ };
template <std::size_t N, std::size_t ... Next>
struct indexSeqHelper
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; };
template <std::size_t ... Next >
struct indexSeqHelper<0U, Next ... >
{ using type = indexSeq<Next ... >; };
and implement fibVals
constructors as follows
template <std::size_t ... Is>
constexpr fibVals ( indexSeq<Is...> const & )
: vals { { fib<Is, T>::value ... } }
{ }
constexpr fibVals () : fibVals { typename indexSeqHelper<I>::type { } }
{ }
Upvotes: 4
Reputation: 48948
Templates are evaluated at compile time, so there is no solution with templates that works at runtime.
You can make a constexpr
function, which may be evaluated at compile time, depending on the value passed. Obviously, a runtime value may not be computed at compile time, as it is not known at compile time.
Upvotes: 1