Variadic template pack expansion argument id - c++

I am trying to make a variadic template function that reads elements in order (with an index). The goal is, for example to call the function read_tuple to read two ints with id 0 and 1 (with read_int(0) and read_int(1)).
Here is the code I get so far:
int data[] = {10,20,30,40};
int int_read(int id)
{
return data[id];
}
template <typename T>
T read(int& index)
{
index--;
int value = int_read(index);
std::cout << "index :" << index << " value: " << value << std::endl;
return value;
}
template <typename... Args>
std::tuple<Args...> read_tuple()
{
int index = sizeof...(Args);
return std::tuple<Args...>(read<Args>(index)...);
}
I can call it like that:
auto tuple = read_tuple<int, int>();
std::cout << "First: " << std::get<0>(tuple) << std::endl;
And I get the following output:
index :1 value: 20
index :0 value: 10
First: 10
However, this code is dependent of the order of evaluation of the read function. How can I generate an index dependent of the pack expansion (to avoid undefined behavior)?

As Piotr pointed out, the order of evaluation is garanteed if you use braced-init-list. Be carefull though if you use GCC prior to 4.9.1 as it does not work (c.f. Evaluation order (sequenced-before relation) among initializer-clauses in braced-init-list).
If you use a version that does not garantee the order of initialization (or just want to generate ids) you can use an indexer (from other stackoverflow post):
template<int... I> struct index {
template<int n> using append = index<I..., n>;
};
template<int N> struct make_index {
typedef typename make_index<N - 1>::type::template append<N - 1> type;
};
template<> struct make_index<0> { typedef index<> type; };
template<int N> using indexer = typename make_index<N>::type;
You can use it like that:
int data[] = {10,20,30,40};
int int_read(int id)
{
return data[id];
}
template <typename T>
T read(int index)
{
int value = int_read(index);
std::cout << "index :" << index << " value: " << value << std::endl;
return value;
}
template <typename... Args, int... i>
std::tuple<Args...> read_tuple_indexed(index<i...>)
{
return std::tuple<Args...>(read<Args>(i)...);
}
template <typename... Args>
std::tuple<Args...> read_tuple()
{
return read_tuple_indexed<Args...>(indexer<(sizeof...(Args))>());
}

Related

Hashing types at compile-time in C++17/C++2a

Consider the following code:
#include <iostream>
#include <type_traits>
template <class T>
constexpr std::size_t type_hash(T) noexcept
{
// Compute a hash for the type
// DO SOMETHING SMART HERE
}
int main(int argc, char* argv[])
{
auto x = []{};
auto y = []{};
auto z = x;
std::cout << std::is_same_v<decltype(x), decltype(y)> << std::endl; // 0
std::cout << std::is_same_v<decltype(x), decltype(z)> << std::endl; // 1
constexpr std::size_t xhash = type_hash(x);
constexpr std::size_t yhash = type_hash(y);
constexpr std::size_t zhash = type_hash(z);
std::cout << (xhash == yhash) << std::endl; // should be 0
std::cout << (yhash == zhash) << std::endl; // should be 1
return 0;
}
I would like the type_hash function to return a hash key unique to the type, at compile-time. Is there a way to do that in C++17, or in C++2a (ideally only relying on the standard and without relying compiler intrinsics)?
I doubt that's possible with purely the standard C++.
But there is a solution that will work on most major compilers (at least GCC, Clang, and MSVC). You could hash strings returned by the following function:
template <typename T> constexpr const char *foo()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
I don't know a way to obtain a std::size_t for the hash.
But if you accept a pointer to something, maybe you can take the address of a static member in a template class.
I mean... something as follows
#include <iostream>
#include <type_traits>
template <typename>
struct type_hash
{
static constexpr int i { };
static constexpr int const * value { &i };
};
template <typename T>
static constexpr auto type_hash_v = type_hash<T>::value;
int main ()
{
auto x = []{};
auto y = []{};
auto z = x;
std::cout << std::is_same_v<decltype(x), decltype(y)> << std::endl; // 0
std::cout << std::is_same_v<decltype(x), decltype(z)> << std::endl; // 1
constexpr auto xhash = type_hash_v<decltype(x)>;
constexpr auto yhash = type_hash_v<decltype(y)>;
constexpr auto zhash = type_hash_v<decltype(z)>;
std::cout << (xhash == yhash) << std::endl; // should be 0
std::cout << (xhash == zhash) << std::endl; // should be 1
} // ...........^^^^^ xhash, not yhash
If you really want type_hash as a function, I suppose you could simply create a function that return the type_hash_v<T> of the type received.
Based on HolyBlackCat answer, a constexpr template variable which is a (naive) implementation of the hash of a type:
template <typename T>
constexpr std::size_t Hash()
{
std::size_t result{};
#ifdef _MSC_VER
#define F __FUNCSIG__
#else
#define F __PRETTY_FUNCTION__
#endif
for (const auto &c : F)
(result ^= c) <<= 1;
return result;
}
template <typename T>
constexpr std::size_t constexpr_hash = Hash<T>();
Can be used as shown below:
constexpr auto f = constexpr_hash<float>;
constexpr auto i = constexpr_hash<int>;
Check on godbolt that the values are indeed, computed at compile time.
I will agree with the other answers that it's not generally possible as-stated in standard C++ yet, but we may solve a constrained version of the problem.
Since this is all compile-time programming, we cannot have mutable state, so if you're willing to use a new variable for each state change, then something like this is possible:
hash_state1 = hash(type1)
hash_state2 = hash(type2, hash_state1)
hash_state3 = hash(type3, hash_state2)
Where "hash_state" is really just a unique typelist of all the types we've hashed so far. It can also provide a size_t value as a result of hashing a new type.
If a type that we seek to hash is already present in the typelist, we return the index of that type.
This requires quite a bit of boilerplate:
Ensuring types are unique within a typelist: I used #Deduplicator's answer here: https://stackoverflow.com/a/56259838/27678
Finding a type in a unique typelist
Using if constexpr to check if a type is in the typelist (C++17)
Live Demo
Part 1: a unique typelist:
Again, all credit to #Deduplicator's answer here on this part. The following code saves compile-time performance by doing lookups on a typelist in O(log N) time thanks to leaning on the implementation of tuple-cat.
The code is written almost frustratingly generically, but the nice part is that it allows you to work with any generic typelist (tuple, variant, something custom).
namespace detail {
template <template <class...> class TT, template <class...> class UU, class... Us>
auto pack(UU<Us...>)
-> std::tuple<TT<Us>...>;
template <template <class...> class TT, class... Ts>
auto unpack(std::tuple<TT<Ts>...>)
-> TT<Ts...>;
template <std::size_t N, class T>
using TET = std::tuple_element_t<N, T>;
template <std::size_t N, class T, std::size_t... Is>
auto remove_duplicates_pack_first(T, std::index_sequence<Is...>)
-> std::conditional_t<(... || (N > Is && std::is_same_v<TET<N, T>, TET<Is, T>>)), std::tuple<>, std::tuple<TET<N, T>>>;
template <template <class...> class TT, class... Ts, std::size_t... Is>
auto remove_duplicates(std::tuple<TT<Ts>...> t, std::index_sequence<Is...> is)
-> decltype(std::tuple_cat(remove_duplicates_pack_first<Is>(t, is)...));
template <template <class...> class TT, class... Ts>
auto remove_duplicates(TT<Ts...> t)
-> decltype(unpack<TT>(remove_duplicates<TT>(pack<TT>(t), std::make_index_sequence<sizeof...(Ts)>())));
}
template <class T>
using remove_duplicates_t = decltype(detail::remove_duplicates(std::declval<T>()));
Next, I declare my own custom typelist for using the above code. A pretty straightforward empty struct that most of you have seen before:
template<class...> struct typelist{};
Part 2: our "hash_state"
"hash_state", which I'm calling hash_token:
template<size_t N, class...Ts>
struct hash_token
{
template<size_t M, class... Us>
constexpr bool operator ==(const hash_token<M, Us...>&)const{return N == M;}
constexpr size_t value() const{return N;}
};
Simply encapsulates a size_t for the hash value (which you can also access via the value() function) and a comparator to check if two hash_tokens are identical (because you can have two different type lists but the same hash value. e.g., if you hash int to get a token and then compare that token to one where you've hashed (int, float, char, int)).
Part 3: type_hash function
Finally our type_hash function:
template<class T, size_t N, class... Ts>
constexpr auto type_hash(T, hash_token<N, Ts...>) noexcept
{
if constexpr(std::is_same_v<remove_duplicates_t<typelist<Ts..., T>>, typelist<Ts...>>)
{
return hash_token<detail::index_of<T, Ts...>(), Ts...>{};
}
else
{
return hash_token<N+1, Ts..., T>{};
}
}
template<class T>
constexpr auto type_hash(T) noexcept
{
return hash_token<0, T>{};
}
The first overload is for the generic case; you've already "hashed" a number of types, and you want to hash yet another one. It checks to see if the type you're hashing has already been hashed, and if so, it returns the index of the type in the unique type list.
To accomplish getting the index of a type in a typelist, I used simple template expansion to save some compile time template instantiations (avoiding a recursive lookup):
// find the first index of T in Ts (assuming T is in Ts)
template<class T, class... Ts>
constexpr size_t index_of()
{
size_t index = 0;
size_t toReturn = 0;
using swallow = size_t[];
(void)swallow{0, (void(std::is_same_v<T, Ts> ? toReturn = index : index), ++index)...};
return toReturn;
}
The second overload of type_hash is for creating an initial hash_token starting at 0.
Usage:
int main()
{
auto x = []{};
auto y = []{};
auto z = x;
std::cout << std::is_same_v<decltype(x), decltype(y)> << std::endl; // 0
std::cout << std::is_same_v<decltype(x), decltype(z)> << std::endl; // 1
constexpr auto xtoken = type_hash(x);
constexpr auto xytoken = type_hash(y, xtoken);
constexpr auto xyztoken = type_hash(z, xytoken);
std::cout << (xtoken == xytoken) << std::endl; // 0
std::cout << (xtoken == xyztoken) << std::endl; // 1
}
Conclusion:
Not really useful in a lot of code, but this may help solve some constrained meta-programming problems.
I don't think it is possible. "hash key unique to the type" sounds like you are looking for a perfect hash (no collisions). Even if we ignore that size_t has a finite number of possible values, in general we can't know all the types because of things like shared libraries.
Do you need it to persist between runs? If not, you can set up a registration scheme.
I would like to improve upon #max66's answer (which is absolutely brilliant and simple by the way, I wish I thought of it)
<TL;DR>
Put inline on the static variables in max66's answer and it will make sure the same static value is present across all translation units.
</TL;DR>
If you have multiple translation units, then, typically, multiple instances of a static variable (in max66's case i) will be created, changing the value of the hash, this could be a problem, say I have a function getHash that I want to use to check if two types are the same
#include <iostream>
#include <type_traits>
#include <memory>
// <From max66's answer>
template <typename>
struct type_hash
{
static constexpr int i { };
static constexpr int const * value { &i };
};
template <typename T>
static constexpr auto type_hash_v = type_hash<T>::value;
// </From max66's answer>
struct AnyBase {
using PointerType = std::unique_ptr<AnyBase>;
constexpr virtual bool equal(const PointerType& other) const noexcept = 0;
constexpr virtual const int* typeHash() const noexcept = 0;
};
template<typename ParameterType>
struct Any : public AnyBase
{
using BasePointerType = std::unique_ptr<AnyBase>;
using Type = ParameterType;
Type value;
constexpr Any(Type value) noexcept : value(value) {}
constexpr virtual bool equal(const BasePointerType& other) const noexcept final
{
if(other->typeHash() == typeHash()) {
const auto unwrapped = dynamic_cast<const Any<Type>*>(other.get());
return unwrapped->value == value;
}
return false;
}
constexpr virtual const int* typeHash() const noexcept final {
return type_hash_v<Type>;
}
};
using AnyType = std::unique_ptr<AnyBase>;
template<typename ParameterType>
AnyType makeAny(auto... initializationValues) {
return static_cast<AnyType>(std::make_unique<Any<ParameterType>>(initializationValues...));
}
int main()
{
AnyType any0 = makeAny<int>(4);
AnyType any1 = makeAny<int>(2);
AnyType any2 = makeAny<int>(4);
AnyType any3 = makeAny<char>('A');
std::cout << "Hash Codes: "
<< any0->typeHash() << "\n"
<< any1->typeHash() << "\n"
<< any2->typeHash() << "\n"
<< any3->typeHash() << "\n";
std::cout
<< "any0 == any1? " << any0->equal(any1) << "\n" // False within translation unit
<< "any0 == any2? " << any0->equal(any2) << "\n" // True within translation unit
<< "any0 == any3? " << any0->equal(any3) << "\n"; // False within translation unit
return 0;
}
If I instantiate two Any<int>'s in two different translation units, they may have two different hashes, because i is static and is likely to have a different addresses across the translation units, therefore when I try to compare Any<int>'s it will fail even if they have the same type and value.
I learned from Daisy Hollman's presentation at CppNow (slides here) that the C++ standard guarantees a single persistent instantiation of an object across translation units
Unfortunately the way she does type registration at the beginning of the presentation is not constexprable because it relies on a static mutable variable within a function. However using this knowledge we can tweak max66's approach, lets modify the code from before
#include <iostream>
#include <type_traits>
#include <memory>
#include <bit>
template<typename ParameterType>
struct Registrar
{
constexpr inline static const uintptr_t getHash() { // ACCORDING TOO C++ STANDARD INLINE GUARANTEES ONE COPY ACROSS ALL TRANSLATION UNITS
return std::bit_cast<uintptr_t>(&hashObject);
}
protected:
constinit inline static const size_t hashObject = 0; // ACCORDING TOO C++ STANDARD INLINE GUARANTEES ONE COPY ACROSS ALL TRANSLATION UNITS
};
struct AnyBase {
using PointerType = std::unique_ptr<AnyBase>;
constexpr virtual bool equal(const PointerType& other) const noexcept = 0;
constexpr virtual const uintptr_t typeHash() const noexcept = 0;
};
template<typename ParameterType>
struct Any : public AnyBase
{
using BasePointerType = std::unique_ptr<AnyBase>;
using Type = ParameterType;
Type value;
constexpr Any(Type value) noexcept : value(value) {}
constexpr virtual bool equal(const BasePointerType& other) const noexcept final
{
if(other->typeHash() == typeHash()) {
const auto unwrapped = dynamic_cast<const Any<Type>*>(other.get());
return unwrapped->value == value;
}
return false;
}
constexpr virtual const uintptr_t typeHash() const noexcept final {
return Registrar<Type>::getHash();
}
};
using AnyType = std::unique_ptr<AnyBase>;
template<typename ParameterType>
AnyType makeAny(auto... initializationValues) {
return static_cast<AnyType>(std::make_unique<Any<ParameterType>>(initializationValues...));
}
int main()
{
AnyType any0 = makeAny<int>(4);
AnyType any1 = makeAny<int>(2);
AnyType any2 = makeAny<int>(4);
AnyType any3 = makeAny<char>('A');
std::cout << "Hash Codes: "
<< any0->typeHash() << "\n"
<< any1->typeHash() << "\n"
<< any2->typeHash() << "\n"
<< any3->typeHash() << "\n";
std::cout
<< "any0 == any1? " << any0->equal(any1) << "\n" // False GUARANTEED across translation units
<< "any0 == any2? " << any0->equal(any2) << "\n" // True GUARANTEED across translation units
<< "any0 == any3? " << any0->equal(any3) << "\n"; // False GUARANTEED across translation units
return 0;
}
Now our hash is guaranteed across translation units (as I stated in all caps :) )
Thanks to #max66 and Daisy Hollman
And a note, I think you could further static_cast to size_t or something if you want from uintptr_t, both examples compile with gcc 12.2 with -std=c++23

Print spaces between each element using a fold expression

I am using a fold expression to print elements in a variadic pack, but how do I get a space in between each element?
Currently the output is "1 234", the desired output is "1 2 3 4"
template<typename T, typename Comp = std::less<T> >
struct Facility
{
template<T ... list>
struct List
{
static void print()
{
}
};
template<T head,T ... list>
struct List<head,list...>
{
static void print()
{
std::cout<<"\""<<head<<" ";
(std::cout<<...<<list);
}
};
};
template<int ... intlist>
using IntList = typename Facility<int>::List<intlist...>;
int main()
{
using List1 = IntList<1,2,3,4>;
List1::print();
}
If you need space only between numbers (and not after the last or before the first too), you might do:
template <std::size_t... Is>
void print_seq(std::index_sequence<Is...>)
{
const char* sep = "";
(((std::cout << sep << Is), sep = " "), ...);
}
Demo
(It is similar to my "runtime version") for regular containers with for-loop.
you can that
#include <iostream>
template<typename T>
struct Facility
{
template<T head,T ... list>
struct List
{
static void print()
{
std::cout<<"\"" << head;
((std::cout << " " << list), ...);
std::cout<<"\"";
}
};
};
template<int ... intlist>
using IntList = typename Facility<int>::List<intlist...>;
int main()
{
using List1 = IntList<1,2,3,4>;
List1::print();
}
the fold expression ((std::cout << " " << list), ...) will expands to ((std::cout << " " << list1), (std::cout << " " << list2), (std::cout << " " << list3)...)
In general, you use recursion for tasks like this.
You have to define what happens when there are 2 or more and 1 elements in the list and recursively fall back to those definitions:
template <int ...> struct List;
template <int First, int Second, int ... More> struct List {
static void print() {
std::cout << First << " ";
List<Second, More ...>::print();
}
};
template <int Last> struct List {
static void print() {
std::cout << Last;
}
};
You can reuse print() to achieve this behaviour. Afterall you are doing a fold operation which is by definition resursive.
Live Demo
template<T head,T ... rest_of_pack>
struct List<head , rest_of_pack...>
{
static void print_()
{
std::cout<<head<<" ";
List<rest_of_pack...>::print();
}
};
If you want to process many elements this way you might run into problems with template depth (gcc for instance has a limit of 900). Lucky for you you can use the -ftemplate-depth= option to tweak this behaviour.
You can compile with -ftemplate-depth=100000 and make it work. Note that compilation time will skyrocket (most likely) or in thhe worst case you run out of memory.
Not perfectly aligned with the question but I think may be useful putting here a solution based on fold expressions that generates a string from string-like arguments that should minimize dynamic memory allocations:
#include <iostream>
#include <concepts> // std::convertible_to
#include <string>
#include <string_view>
using namespace std::literals;
template<std::convertible_to<std::string_view>... Args>
[[nodiscard]] std::string mystrconcat(Args&&... args)
{
std::string s;
const std::size_t totsiz = sizeof...(args) + (std::size(args) + ...);
s.reserve(totsiz);
((s+=args, s+=' '), ...);
if(!s.empty()) s.resize(s.size()-1); // Get rid of the trailing delimiter?
return s;
}
int main()
{
std::cout << '\"' << mystrconcat("aa"s,"bb"sv,"ccc") << '\"' << '\n';
}

how to expand a statement multiple times based on template variable arguments

I have the following pseudo code:
template <typename... Ts>
void f(int index) {
std::vector<std::function<void(void)>> funcs;
funcs.push_back([](){ std::cout << typeid(type_1).name() << std::endl; });
funcs.push_back([](){ std::cout << typeid(type_2).name() << std::endl; });
funcs.push_back([](){ std::cout << typeid(type_3).name() << std::endl; });
funcs.push_back([](){ std::cout << typeid(type_4).name() << std::endl; });
funcs[index]();
}
Imagine that the Ts... parameter pack holds type_1, type_2, type_3 and type_4.
how can I expand the parameter pack in order to achieve something like this? I mean - how can I get 4 push_back() calls if there are 4 parameters in the template pack, and also have the different types in the different lambdas? I don't know the syntax..
And can I actually get some sort of an array of such functions at compile time, so there are no push_backs at runtime?
C++17 solution is ok, but C++14 is best.
For C++17, something like this, I suppose
(funcs.push_back([](){ std::cout << typeid(Ts).name() << std::endl; }), ...);
or, better (IMHO), using emplace_back()
(funcs.emplace_back([](){ std::cout << typeid(Ts).name() << std::endl; }), ...);
But remeber that is
std::vector<std::function<void(void)>>
not
std::vector<std::function<void>>
In C++14 (and C++11) you can obtain something similar with the trick of intialization of the unused array; the function can be written as
template <typename ... Ts>
void f (int index)
{
using unused = int[];
std::vector<std::function<void(void)>> funcs;
(void)unused { 0, (funcs.emplace_back([]()
{ std::cout << typeid(Ts).name() << std::endl; }), 0)... };
funcs[index]();
}
Update.
From re-reading the question I think you just want to call the function once for the I'th type.
I which case it's trivial at compile time:
#include <array>
#include <type_traits>
#include <iostream>
#include <string>
template <class T>
void show_type()
{
std::cout << typeid(T).name() << std::endl;
}
template <typename... Ts>
void f(int index) {
using function_type = void(*)();
constexpr auto size = sizeof...(Ts);
constexpr std::array<function_type, size> funcs =
{
&show_type<Ts>...
};
funcs[index]();
}
int main()
{
for(int i = 0 ; i < 3 ; ++i)
f<int, double, std::string>(i);
}
example output:
i
d
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Something along these lines, perhaps:
template <typename... Ts>
void f(int index) {
int i = 0;
auto _ = {
(index == i++ ? ((std::cout << typeid(Ts).name() << std::endl) , 0) : 0) ...
};
}
Demo
If all you want to do is do something for the nth type in a template parameter pack, where n is a runtime variable, then the vector + function approach isn't really great. Better to add an index sequence in there and fold:
template <typename T> struct tag_t { using type = T; };
template <typename T> constexpr inline tag_t<T> tag{};
template <class F, size_t... Is, typename... Tags>
void match(F f, size_t i, std::index_sequence<Is...>, Tags... tags) {
auto inner = [&](auto tag) { f(tag); return true; };
bool matched = ((i == Is && inner(tags)) || ...);
if (!matched) {
// failure case?
}
}
template <typename... Ts, class F>
void match(F f, size_t i) {
return match(f, i, std::index_sequence_for<Ts...>(), tag<Ts>... );
}
template <typename... Ts>
void foo(int index) {
match<Ts...>([](auto tag){
std::cout << typeid(typename decltype(tag)::type).name() << std::endl;
}, index);
}
This construction allows you to add a failure case, where you might call the passed-in function with some special type:
struct failure { };
template <class F, size_t... Is, typename... Tags>
void match(F f, size_t i, std::index_sequence<Is...>, Tags... tags) {
auto inner = [&](auto tag) { f(tag); return true; };
bool matched = ((i == Is && inner(tags)) || ...);
if (!matched) {
f(failure{});
}
}
template <typename... Ts>
void foo(int index) {
match<Ts...>(overload(
[](auto tag){
std::cout << typeid(typename decltype(tag)::type).name() << std::endl;
},
[](failure ) { /* ... */ }
), index);
}

How pass the same struct with different template argument as parameter pack

Thanks to SO guys I resolved one of my problem:
Create a tuple with variatic type wrapped
But I realized after that, I still have a problem that I can't solve.
So now I have :
template < typename T,
size_t Size >
struct Metadata {
using type = T;
std::bitset<Size> bitset;
};
template <class... Ts>
constexpr auto make_metadata()
{
constexpr size_t N = sizeof...(Ts);
return std::make_tuple(Metadata<Ts, N>{0}...);
}
I intent to use it "like" that :
constexpr auto result = make_metadata<Foo1, Foo2, Foo3>({0}, {0}, {0});
And according to Jarod42 comment, I think I'll need 2 functions.
But how I can pass the arguments to the function and then to the tuple?
And I wonder how do that but without force to pass each arguments for each Ts, if they are not present I'll just put a default value (2 questions).
this may be what you want, I am not sure. (since you never show where the arguments to be used)
#include <tuple>
#include <bitset>
#include <iostream>
struct A{int value;};
struct B{int value;};
struct C{int value;};
template <typename T,int Size>
struct Metadata{
using type = T;
T value;
std::bitset<Size> bitset;
};
template <typename...Ts,typename...Args>
constexpr auto make_metadata(Args... args)
{
constexpr auto N = sizeof...(Ts);
return std::make_tuple(Metadata<Ts, N>{args,0}...);
}
int main(){
auto data = make_metadata<A,B,C>(1,2,3);
std::cout << "(" << std::get<0>(data).value.value
<< ", " << std::get<1>(data).value.value
<< ", " << std::get<2>(data).value.value << ")";
}

calculating data compile time with template metaprogramming

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?
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 { } }
{ }
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.