I want to be able to create switch statements over a type's ID. I've found a mechanism that could give a unique ID for different types. It's very simple:
template <typename T>
struct type {
static void id() { }
};
template <typename T>
constexpr const size_t type_id() {
return reinterpret_cast<size_t>(&type<T>::id);
}
I thought this would evaluate to a constant that I could use as cases for the switch. But I get an error that the case expression is not a constant when I do the following:
int main(void) {
size_t a = type_id<int>();
switch (a) {
case type_id<int>():
break;
}
return 0;
}
Why is it not a constant? How could I achieve this effect?
Edit:
Can I do something like this without the reinterpret_cast then?
I'm not sure it's a good idea but... just for fun... using the constexpr counter, suggested in this page, you should be able to substitute the value of the pointer.
The following is a (I repeat: just for fun) full experiment
#include <iostream>
template <int N>
struct flag
{ friend constexpr int adl_flag (flag<N>); };
template <int N>
struct writer
{
friend constexpr int adl_flag (flag<N>)
{ return N; }
static constexpr int value { N };
};
template <int N, int = adl_flag (flag<N> {})>
int constexpr reader (int, flag<N>)
{ return N; }
template <int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1> {}))
{ return R; }
int constexpr reader (float, flag<0>)
{ return 0; }
template <int N = 1>
int constexpr next (int R = writer<reader (0, flag<32> {}) + N>::value)
{ return R; }
template <typename T>
struct type
{
static constexpr int id { next() };
constexpr static int type_id ()
{ return id; }
};
void printType (int idT )
{
switch ( idT )
{
case type<int>::type_id():
std::cout << "- int type" << std::endl;
break;
case type<long>::id:
std::cout << "- long type" << std::endl;
break;
default:
std::cout << "- another type" << std::endl;
break;
}
}
int main ()
{
int ii { type<int>::id };
int il { type<long>::type_id() };
printType(ii);
printType(il);
}
I would like to suggest another approach which involves constexpr functions and macros (eeeewww...):
// Some naive text hashing function
template <std::size_t SIZE>
constexpr std::size_t hash(const char (&type_name)[SIZE])
{
std::size_t result{0xf};
for (const auto &c : type_name)
{
result <<= 1;
result |= c;
}
return result;
}
First we create a constexpr function able to transform a string literal into a number, this is my approach but you can choose anoter function as long as it is constexpr, then we create a macro which stringify the given parameter using the #:
#define TYPE_ID(X) hash(#X)
And now, we can use it:
int main(void) {
size_t a = TYPE_ID(int);
switch (a) {
case TYPE_ID(int):
break;
}
return 0;
}
Pros:
Pretty straightforward.
Tiny amount of code.
Cons:
Macros.
Accepts any value, included nonsense: TYPE_ID(I LOVE BACON) is valid.
Yields different result for TYPE_ID(size_t) and TYPE_ID(unsigned long) even if they might be the same type.
constexpr functions can not use reinterpret_cast in any shape or form. Some more formal reading can be found at http://en.cppreference.com/w/cpp/language/constant_expression
This may solve your problem:
#include <tuple>
//Index from http://stackoverflow.com/a/18063608/3484570
template <class T, class Tuple>
struct Index;
template <class T, class... Types>
struct Index<T, std::tuple<T, Types...>> {
static const std::size_t value = 0;
};
template <class T, class U, class... Types>
struct Index<T, std::tuple<U, Types...>> {
static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
};
template <class T>
constexpr std::size_t type_id() {
//need to add every type in this tuple:
return Index<T, std::tuple<int, double, char>>::value;
}
int main() {
size_t a = type_id<int>();
switch (a) {
case type_id<int>():
break;
}
}
The good news is that you get a type_id<T>() that you can use in a constexpr context such as in a case like you wanted.
The bad news is that you need to list all supported types.
In practice you might get used to the error message that occurs when you ask for the type_id of an unsupported type and just add it and eventually add all relevant types.
Related
I have many functions q1, q2, q3, etc., each with a different return type (int, int64_t, std::string, etc.).
I also have a print_result function that prints out their results (and the time they take to run, but trimmed here for simplicity):
template <typename T>
void print_result(T (*func)()) {
T res = func();
std::cout << res << std::endl;
}
I also have big switch statement to print the result for each of the functions:
switch (question_num) {
case 1: print_result(q1); break;
case 2: print_result(q2); break;
case 3: print_result(q3); break;
// ...
}
Objective: I would like to replace this switch statement with a template function, to avoid copying each line every time I add a new function.
I have tried to look at C++ template instantiation: Avoiding long switches, but I'm new to template metaprogramming, so not sure how to handle this exactly.
My current attempt that doesn't compile:
template <<int, typename> ...> struct FuncList {};
template <typename T>
bool handle_cases(int, T, FuncList<>) {
// default case
return false;
}
template <<int I, typename T> ...S>
bool handle_cases(int i, T (*func)(), FuncList<T, S...>) {
if (I != i) {
return handle_cases(i, func, FuncList<S...>());
}
print_result(func);
return true;
}
template <typename ...S>
bool handle_cases(int i, T (*func)()) {
return handle_cases(i, func, FuncList<S...>());
}
// ...
bool res = handle_cases<
<1, q1>, <2, q2>, <3, q3>
>(question_num);
// ...
My ideal way of using this template is shown at the last line there.
Note that the mappings from the function number to the function is provided there. The function numbers are fixed, i.e. q1 maps to the constant 1 and that won't change at runtime.
The compilation error (it might be rather basic but I really don't know much about metaprogramming):
error: expected unqualified-id before ‘<<’ token
17 | template <<int, typename> ...> struct FuncList {};
| ^~
If you can use c++17, here's a "simplified" version of #Klaus's approach. Instead of using a had-made recursive structure, you could use a c++17 fold-expression:
template<auto... Funcs, std::size_t... I>
bool select_case(std::size_t i, std::integer_sequence<std::size_t, I...>) {
return ([&]{ if(i == I) { print_result(Funcs); return true; } return false; }() || ... );
}
template<auto... Funcs>
struct FuncSwitch {
static bool Call(std::size_t i) {
return select_case<Funcs...>(i, std::make_index_sequence<sizeof...(Funcs)>());
}
};
The idea is to wrap each of Funcs in a lambda such that only the function corresponding to the index passed is called. Note that the || in the fold expression short-circuits.
Would be used like this:
float q0() { return 0.f; }
int q1() { return 1; }
std::string q2() { return "two"; }
int main() {
bool success = FuncSwitch<q0, q1, q2>::Call(1);
}
See here for a complete example.
I've got a different proposal:
Use an std::array instead of switch (or std::map if the switch cases are non-continuous, std::array has O(1) access time, std::map O(log(n)) and switch O(n).
Use std::function and std::bind to bind your functions you want to call to a functor object
use the index into the array to call the function
Use placeholders if you need to pass additional data
#include <iostream>
#include <functional>
template <typename T>
void print_result(T (*func)()) {
T res = func();
std::cout << res << std::endl;
}
int int_function() {
return 3;
}
double double_function() {
return 3.5;
}
std::array<std::function<void()>, 2> functions({
std::bind(print_result<int>, int_function),
std::bind(print_result<double>, double_function),
});
int main() {
functions[0]();
functions[1]();
return 0;
}
Output:
3
3.5
See: Why does std::function can implicit convert to a std::function which has more parameter?
Update:
With parameter passing:
#include <iostream>
#include <functional>
template <typename T>
void print_result(T (*func)(int), int value) {
T res = func(value);
std::cout << res << std::endl;
}
int int_function(int value) {
return 3 * value;
}
double double_function(int value) {
return 3.5 * value;
}
std::array<std::function<void(int)>, 2> functions({
std::bind(print_result<int>, int_function, std::placeholders::_1),
std::bind(print_result<double>, double_function, std::placeholders::_1),
});
int main() {
functions[0](10);
functions[1](11);
return 0;
}
Output:
30
38.5
You may like a version which do not need any kind of runtime containers, did not generate any objects in between and even do not generate a data table and generates very less code and is also easy to use:
// Example functions
int fint() { return 1; }
double fdouble() { return 2.2; }
std::string fstring() { return "Hallo"; }
// your templated result printer
template < typename T>
void print_result( T parm )
{
std::cout << "The result of call is " << parm << std::endl;
}
// lets create a type which is able to hold functions
template < auto ... FUNCS >
struct FUNC_CONTAINER
{
static constexpr unsigned int size = sizeof...(FUNCS);
};
// and generate a interface to switch
template < unsigned int, typename T >
struct Switch_Impl;
template < unsigned int IDX, auto HEAD, auto ... TAIL >
struct Switch_Impl< IDX, FUNC_CONTAINER<HEAD, TAIL...>>
{
static void Do( unsigned int idx )
{
if ( idx == IDX )
{
// Your function goes here
print_result(HEAD());
}
else
{
if constexpr ( sizeof...(TAIL))
{
Switch_Impl< IDX+1, FUNC_CONTAINER<TAIL...>>::Do(idx);
}
}
}
};
// a simple forwarder to simplify the interface
template < typename T>
struct Switch
{
static void Do(unsigned int idx )
{
Switch_Impl< 0, T >::Do( idx );
}
};
// and lets execute the stuff
int main()
{
using FUNCS = FUNC_CONTAINER< fint, fdouble, fstring >;
for ( unsigned int idx = 0; idx< FUNCS::size; idx++ )
{
Switch<FUNCS>::Do(idx);
}
}
Given you "current attempt"... it seems to me that you could write a handle_cases struct/class almost as follows
struct handle_cases
{
std::map<int, std::function<void()>> m;
template <typename ... F>
handle_cases (std::pair<int, F> const & ... p)
: m{ {p.first, [=]{ print_result(p.second); } } ... }
{ }
void operator() (int i)
{ m[i](); }
};
with a map between an integer and a lambda that call print_result with the function and an operator() that call the requested lambda, given the corresponding index.
You can create an object of the class as follows (unfortunately I don't see a way to avoid the std::make_pair()s)
handle_cases hc{ std::make_pair(10, q1),
std::make_pair(20, q2),
std::make_pair(30, q3),
std::make_pair(40, q4) };
and using it as follows
hc(30);
The following is a full compiling example
#include <functional>
#include <map>
#include <iostream>
template <typename T>
void print_result (T(*func)())
{
T res = func();
std::cout << res << std::endl;
}
struct handle_cases
{
std::map<int, std::function<void()>> m;
template <typename ... F>
handle_cases (std::pair<int, F> const & ... p)
: m{ {p.first, [=]{ print_result(p.second); } } ... }
{ }
void operator() (int i)
{ m[i](); }
};
char q1 () { return '1'; }
int q2 () { return 2; }
long q3 () { return 3l; }
long long q4 () { return 4ll; }
int main ()
{
handle_cases hc{ std::make_pair(10, q1),
std::make_pair(20, q2),
std::make_pair(30, q3),
std::make_pair(40, q4) };
hc(30);
}
I'm working on a C++11 wrapper around a C api. The C api offers a bunch of getters for various types, with a different name for each type. Values are retrieved by array of a given size, known at compilation.
I want to give the type and the array size by template, to call the right function.
#include <string>
#include <iostream>
template <typename T>
struct make_stop {
constexpr static bool value = false;
};
class Foo
{
public:
Foo() : i(42) {}
template<typename T, size_t n>
T get();
private:
int i = 0;
};
template<typename T, size_t n>
T Foo::get() { static_assert(make_stop<T>::value); return T(); }
template<int, size_t n>
int Foo::get() { return i + n; }
int main() {
Foo foo;
int i = foo.get<int, 4>();
double f = foo.get<double, 2>();
return 0;
}
But it fails to match the right function
main.cpp:26:5: error: no declaration matches 'int Foo::get()'
int Foo::get() { return i + n; }
^~~
main.cpp:15:7: note: candidate is: 'template<class T, long unsigned int n> T Foo::get()'
T get();
its a bit vauge from your question, but assuming you are wanting to index into some c- arrays and return the value at I you can't specialize function templates like you want, but you can use some tags instead, something like..
class Foo
{
public:
Foo() : is{1,2,3,4,5,6,7,8,9,10},ds{1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.1} {}
template <typename T> struct type_c{};
template <size_t I> struct int_c{};
template<typename T,size_t I>
auto get()
{ return get_impl(type_c<T>(),int_c<I>()); }
private:
template <size_t I>
auto get_impl(type_c<int>,int_c<I>)
{ return is[I]; }
template <size_t I>
auto get_impl(type_c<double>,int_c<I>)
{ return ds[I]; }
int is[10];
double ds[10];
};
int main() {
Foo foo;
int i = foo.get<int,0>();
double d = foo.get<double,2>();
std::cout << i << " " << d << std::endl;
return 0;
}
Demo
If I understood you correctly you want to partially specialize get for T. Unfortunately partial specialization for methods is not allowed by the standard. You can however get around this with a static method on a class templated by T and specializing the class.
Like this:
template <class T> struct Foo_helper;
struct Foo
{
Foo() : i{42} {}
template<class T, std::size_t N>
T get()
{
return Foo_helper<T>::template get<N>(*this);
}
int i = 0;
};
template <class T> struct Foo_helper {};
// specialize Foo_helper for each type T you wish to support:
template <> struct Foo_helper<int>
{
template <std::size_t N>
static int get(const Foo& foo) { return foo.i + N; }
};
template <> struct Foo_helper<double>
{
template <std::size_t N>
static double get(const Foo& foo) { return foo.i + N; }
};
int main()
{
Foo foo{};
int i = foo.get<int, 4>();
double d = foo.get<double, 2>();
}
I wrote code like this
#include <iostream>
using namespace std;
constexpr int getsum(int to){
int s = 0;
for(int i = 0; i < to; i++){
s += i;
}
return s;
}
int main() {
constexpr int s = getsum(10);
cout << s << endl;
return 0;
}
I understand that it works because of extended constexpr. However in this question why-isnt-a-for-loop-a-compile-time-expression, the author gave his code as follow:
#include <iostream>
#include <tuple>
#include <utility>
constexpr auto multiple_return_values()
{
return std::make_tuple(3, 3.14, "pi");
}
template <typename T>
constexpr void foo(T t)
{
for (auto i = 0u; i < std::tuple_size<T>::value; ++i)
{
std::get<i>(t);
}
}
int main()
{
constexpr auto ret = multiple_return_values();
foo(ret);
}
It could not compile by GCC5.1, however, after replacing std::get<i>(t); with something without a specified template parameter his code did work.
After reading answers under this question I found their main point is constexpr for creates trouble for the compiler so for-loop is used at run-time.
So it makes me confused, on one hand, for-loop is at run-time, on the other, the loop is in a constexpr function, so it must be calculated at compile time, so there seems to be a contradiction. I just wonder where did I make a mistake.
Your function need to be valid both at runtime and at compile time. So your Variant i can be seen as a runtime variable. If the function is executed at compile time, the variable i is part of the compiler's runtime. So a int in a constexpr function is under the same rules as a int in a non-constexpr function.
What you can do is to create your own constexpr for loop:
template<typename F, std::size_t... S>
constexpr void static_for(F&& function, std::index_sequence<S...>) {
int unpack[] = {0,
void(function(std::integral_constant<std::size_t, S>{})), 0)...
};
(void) unpack;
}
template<std::size_t iterations, typename F>
constexpr void static_for(F&& function) {
static_for(std::forward<F>(function), std::make_index_sequence<iterations>());
}
Then, you can use your static_for like this:
static_for<std::tuple_size<T>::value>([&](auto index) {
std::get<index>(t);
});
Note that lambda function cannot be used in constexpr function until C++17, so you can instead roll your own functor:
template<typename T>
struct Iteration {
T& tuple;
constexpr Iteration(T& t) : tuple{t} {}
template<typename I>
constexpr void operator() (I index) const {
std::get<index>(tuple);
}
};
And now you can use static_for like that:
static_for<std::tuple_size<T>::value>(Iteration{t});
This really has nothing to do with whether or not the constexpr function needs to be able to be runnable at compile-time (tuple_size<T>::value is a constant expression regardless of whether the T comes from a constexpr object or not).
std::get<> is a function template, that requires an integral constant expression to be called. In this loop:
for (auto i = 0u; i < std::tuple_size<T>::value; ++i)
{
std::get<i>(t);
}
i is not an integral constant expression. It's not even constant. i changes throughout the loop and assumes every value from 0 to tuple_size<T>::value. While it kind of looks like it, this isn't calling a function with different values of i - this is calling different functions every time. There is no support in the language at the moment for this sort of iteration†, and this is substantively different from the original example, where we're just summing ints.
That is, in one case, we're looping over i and invoking f(i), and in other, we're looping over i and invoking f<i>(). The second one has more preconditions on it than the first one.
†If such language support is ever added, probably in the form of a constexpr for statement, that support would be independent from constexpr functions anyway.
Some better approach as Guillaume Racicot have mentioned with workaround of a bit unfinished constexpr support and std::size in such compilers like Visual Studio 2015 Update 3 and so.
#include <tuple>
#include <stdio.h>
#include <assert.h>
// std::size is supported from C++17
template <typename T, size_t N>
constexpr size_t static_size(const T (&)[N]) noexcept
{
return N;
}
template <typename ...T>
constexpr size_t static_size(const std::tuple<T...> &)
{
return std::tuple_size<std::tuple<T...> >::value;
}
template<typename Functor>
void runtime_for_lt(Functor && function, size_t from, size_t to)
{
if (from < to) {
function(from);
runtime_for_lt(std::forward<Functor>(function), from + 1, to);
}
}
template <template <typename T_> class Functor, typename T>
void runtime_foreach(T & container)
{
runtime_for_lt(Functor<T>{ container }, 0, static_size(container));
}
template <typename Functor, typename T>
void runtime_foreach(T & container, Functor && functor)
{
runtime_for_lt(functor, 0, static_size(container));
}
template <typename T>
void static_consume(std::initializer_list<T>) {}
template<typename Functor, std::size_t... S>
constexpr void static_foreach_seq(Functor && function, std::index_sequence<S...>) {
return static_consume({ (function(std::integral_constant<std::size_t, S>{}), 0)... });
}
template<std::size_t Size, typename Functor>
constexpr void static_foreach(Functor && functor) {
return static_foreach_seq(std::forward<Functor>(functor), std::make_index_sequence<Size>());
}
Usage:
using mytuple = std::tuple<char, int, long>;
template <typename T>
struct MyTupleIterator
{
T & ref;
MyTupleIterator(T & r) : ref(r) {}
void operator() (size_t index) const
{
// still have to do with switch
assert(index < static_size(ref));
size_t value;
switch(index) {
case 0: value = std::get<0>(ref); break;
case 1: value = std::get<1>(ref); break;
case 2: value = std::get<2>(ref); break;
}
printf("%u: %u\n", unsigned(index), unsigned(value));
}
};
template <typename T>
struct MyConstexprTupleIterator
{
T & ref;
constexpr MyConstexprTupleIterator(T & r) : ref(r) {}
constexpr void operator() (size_t index) const
{
// lambda workaround for:
// * msvc2015u3: `error C3250: 'value': declaration is not allowed in 'constexpr' function body`
// * gcc 5.x: `error: uninitialized variable ‘value’ in ‘constexpr’ function`
[&]() {
// still have to do with switch
assert(index < static_size(ref));
size_t value;
switch(index) {
case 0: value = std::get<0>(ref); break;
case 1: value = std::get<1>(ref); break;
case 2: value = std::get<2>(ref); break;
}
printf("%u: %u\n", unsigned(index), unsigned(value));
}();
}
};
int main()
{
mytuple t = std::make_tuple(10, 20, 30);
runtime_foreach<MyTupleIterator>(t);
mytuple t2 = std::make_tuple(40, 50, 60);
runtime_foreach(t2, [&](size_t index) {
// still have to do with switch
assert(index < static_size(t2));
size_t value;
switch(index) {
case 0: value = std::get<0>(t2); break;
case 1: value = std::get<1>(t2); break;
case 2: value = std::get<2>(t2); break;
}
printf("%u: %u\n", unsigned(index), unsigned(value));
});
mytuple t3 = std::make_tuple(70, 80, 90);
static_foreach<std::tuple_size<decltype(t3)>::value>(MyConstexprTupleIterator<mytuple>{t3});
return 0;
}
Output:
0: 10
1: 20
2: 30
0: 40
1: 50
2: 60
0: 70
1: 80
2: 90
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.
Suppose we have function such as
template <typename T, unsigned N> void foo();
and for simplicity assume that we know that only (constant) values N_1, N_2 ... N_k are valid for N.
Now, suppose I want to make that compile-time parameter a run-time one, using foo() as a black-box, i.e. implement:
template <typename T> void foo(unsigned n);
by making foo<,>() calls. How should I go about doing that? Obviously, I can write:
template <typename T> void foo(unsigned n) {
switch(n) {
case N_1 : foo<T, N_1>(); break;
case N_2 : foo<T, N_2>(); break;
// etc. etc.
case N_k : foo<T, N_k>(); break;
}
}
... but this makes me feel all dirty. I could use a MAP() meta-macro to generate these k lines, I suppose; but can I do anything better and less-macroish to achieve the same? Is it possible to write something like the above that's general, and works for every variadic template and a fixed sequence of constant values?
Notes:
C++11/14/17-specific suggestions are obviously welcome.
The N's are not necessarily contiguous, nor small, nor sorted. e.g. suppose N_2 = 123456789 and N_5 = 1.
You could make a function pointer table:
using F = void(*)();
template <class T, class >
struct Table;
template <class T, size_t... Is>
struct Table<T, std::index_sequence<Is...> > {
static constexpr F fns[] = {
foo<T, Is>...
};
};
template <class T, size_t... Is>
constexpr F Table<T, std::index_sequence<Is...> >::fns[sizeof...(Is)];
And then just invoke the one you want:
template <class T, size_t N>
struct MakeTable : Table<T, std::make_index_sequence<N>> { };
template <typename T>
void foo(unsigned n) {
MakeTable<T, MaxN>::fns[n]();
}
If the N_ks aren't contiguous, then we can use a lambda for inline parameter unpacking:
template <class T>
void foo(unsigned n) {
using seq = std::index_sequence<N_1, N_2, ..., N_k>;
indexer(seq)([n](auto i){
if (n == i) {
f<T, i>();
}
});
}
If the above is too slow, then I guess just manually build a std::unordered_map<unsigned, void(*)()> or something.
In these kind of situations I like to build a static table of function pointers, with a dynamic parameter deciding which one to dispatch to. Below is an implementation that achieves this, in the function foo_dynamic. To this function, you specify the maximum value of N you'd like to support, and it builds a static table of function pointers using some recursive templates. You then dereference into this table with your dynamic parameter.
using ftype = void (*)();
template <typename T, unsigned N> void foo()
{
std::cout << N << std::endl;
}
template <typename T, unsigned max>
struct TablePopulator
{
static void populateFTable(ftype* table)
{
table[max] = foo<T,max>;
TablePopulator<T,max-1>::populateFTable(table);
}
};
template <typename T>
struct TablePopulator<T, 0>
{
static void populateFTable(ftype* table)
{
table[0] = foo<T,0>;
}
};
template<typename T, unsigned max_N>
std::array<ftype, max_N>& initTable()
{
static std::array<ftype, max_N> table;
TablePopulator<T, max_N-1>::populateFTable(table.data());
return table;
}
template<typename T, unsigned max_N>
void foo_dynamic(unsigned actualN)
{
static auto ftable = initTable<T, max_N>();
if(actualN >= max_N)
throw std::runtime_error("Max param exceeded");
ftable[actualN]();
}
int main()
{
foo_dynamic<int, 10>(1);
foo_dynamic<int, 10>(5);
return 0;
}
EDIT: Given the constraints in the question edit, here's an approach where valid indices are specified manually, which uses an unordered_map instead of an array:
using ftype = void (*)();
template <typename T, unsigned N> void foo()
{
std::cout << N << std::endl;
}
template<typename T, size_t ... Indices>
void foo_dynamic_indices(size_t actual_index)
{
static std::unordered_map<size_t, ftype> fmap = {{Indices, foo<T,Indices>}...};
auto fIt = fmap.find(actual_index);
if(fIt == fmap.end())
throw std::runtime_error("Index not found");
fIt->second();
}
int main()
{
foo_dynamic_indices<int, 0, 3, 400, 1021, 10000000>(10000000);
foo_dynamic_indices<int, 0, 3, 400, 1021, 10000000>(4); //Exception
return 0;
}