I have a class depending on an integer template parameter. At one point in my program I want to use one instantiation of this template, depending on a value of this paramet
You could use a variadic template, maybe like this:
#include <cstdlib>
#include <string>
int main(int argc, char * argv[])
{
if (argc != 2) { return EXIT_FAILURE; }
handle_cases<1, 3, 4, 9, 11>(std::stoi(argv[1]));
}
Implementation:
template <int ...> struct IntList {};
void handle_cases(int, IntList<>) { /* "default case" */ }
template <int I, int ...N> void handle_cases(int i, IntList<I, N...>)
{
if (I != i) { return handle_cases(i, IntList<N...>()); }
Wrapper<I> w;
w.foo();
}
template <int ...N> void handle_cases(int i)
{
handle_cases(i, IntList<N...>());
}
Here's another approach:
template<int N>
void do_foo()
{
Wrapper<N> w;
w.foo();
}
template<int N, int... Ns>
struct fn_table : fn_table<N - 1, N - 1, Ns...>
{
};
template<int... Ns>
struct fn_table<0, Ns...>
{
static constexpr void (*fns[])() = {do_foo<Ns>...};
};
template<int... Ns>
constexpr void (*fn_table<0, Ns...>::fns[sizeof...(Ns)])();
int main(int argc, char *argv[])
{
std::string arg = argv[1];
int arg_int = std::stoi(arg);
// 4 if you have Wrapper<0> to Wrapper<3>.
fn_table<4>::fns[arg_int]();
}
As an general alternative to switches, you could use a vector or map of function pointers to remove the switch:
template <int i>
int foo()
{
Wrapper<i> w;
w.foo();
return i;
}
static std::vector<int(*)()> m;
void init()
{
m.push_back(&foo<0>);
m.push_back(&foo<1>);
}
void bar(int i)
{
m[i]();
}
In C++11 you could use an initializer list to initialize the vector or map.
arg_int is a runtime parameter so there is no way to attach it directly to a template parameter. You could use some kind of handler table which would remove the switch statement here.
You'd use something like lookup_handler( int N )
returning a type handler
which might be a lambda invoking one of those template functions.
Registering all your lambdas on the table could be done recursively starting with the highest numbered one you allow.
template< unsigned N > register_lambda()
{
table.add( Wrapper<N>() );
register_lambda< N-1 >;
}
and specialise for register_lambda<0>
Then somewhere you call register_lambda<32>
say and you have registered all the numbers from 0 to 32.
One way to implement such a table is:
class lambda_table
{
typedef std::function<void()> lambda_type;
public:
void add( lambda_type );
bool lookup( size_t key, lambda_type & lambda ) const;
};
From main() or wherever you want to invoke it you have a reference to this table (call it table) then call
lambda_type lambda;
if( table.find( arg_int, lambda ) )
lanbda();
else
default_handler();
You might change this to give the table itself a default handler where none has been supplied for this number.
Although lambdas can wrap all kinds of data members you might actually want your templates to be classes in a hierarchy rather than lambdas given the data storage within them.
To explain @Simple's solution that is based on a static function table:
#include <iostream>
#include <vector>
using namespace std;
template<int N>
void do_foo()
{
cout << N << endl;
}
template<int N, int... Ns>
struct fn_table : fn_table<N - 1, N - 1, Ns...> {
};
template<int... Ns>
void p()
{
int a[] = {Ns...};
for (int i = 0; i < sizeof(a)/sizeof(int); ++i)
cout << a[i] << endl;
}
// Recursion-base instantiation with leading 0 parameter.
template<int... Ns>
struct fn_table<0, Ns...> {
// calling fn_table<4> would call recursively with template parameters: <4>, <3, 3>, <2, 2, 3>, <1, 1, 2, 3>, <0, 0, 1, 2, 3>. The last call would create 4 (we expand Ns without the first 0) do_foo functions using a variadic parameter pack "...".
static constexpr void (*fns[])() = {
p<Ns...> // call a function that prints Ns... for illustration, expanding the parameters inside p instead of duplicating it.
//do_foo<Ns>...
};
};
template<int... Ns>
constexpr void (*fn_table<0, Ns...>::fns[sizeof...(Ns)])();
int main(int argc, char *argv[])
{
int arg_int = 0;
// 4 if you have Wrapper<0> to Wrapper<3>.
fn_table<4>::fns[arg_int]();
}
Building the table using an integer_sequence. I also added: i) a sequence start, ii) a parameter for the type of the function, e.g. to receive and return values.
#include <iostream>
#include <vector>
using namespace std;
struct Foo {
template<int N>
static void foo(int &a) {
cout << N << endl;
a = N + 1;
}
};
template<int start, typename F, typename R, typename T, T... ints>
auto fn_table_( integer_sequence<T, ints...> int_seq )
{
vector<R> expand = { F::foo<ints+start>... };
vector<R> dummy( start );
expand.insert( expand.begin(), dummy.begin(), dummy.end() );
return expand;
}
template<int start, typename F, typename R, int N>
auto fn_table()
{
return fn_table_<start, F, R>( make_integer_sequence<int, N-start>{} );
}
void main()
{
int arg_int = 5;
typedef void (*fun_type)( int & );
auto fns = fn_table<4, Foo, fun_type, 7>();
int a;
fns[arg_int]( a );
cout << a << endl;
cout << "all:\n";
for (int i = 0; i < fns.size() ; ++i)
if ( fns[i] )
fns[i]( a );
}