Using an int as a template parameter that is not known until run-time
This can be done in effect. But trust me when I say you are asking the wrong question. So what follows answers your question, even thought doing it is a bad idea almost always.
What you in effect can do is create 50 different programs, one for each of the 50 possible sizes, and then conditionally jump to the one you want.
template<int n>
struct prog {
void run() {
// ...
}
};
template<int n>
struct switcher {
void run(int v) {
if(v==n)
prog<n>::run();
else
switcher<n-1>::run(v);
}
};
template<>
struct switcher<-1> {
void run(int v){
}
};
Call switcher<50>::run( value );
and if value is 0 to 50, prog<value>::run()
is invoked. Within prog::run
the template parameter is a compile time value.
Horrid hack, and odds are you would be better off using another solution, but it is what you asked for.
Here is a C++14 table-based version:
template<size_t N>
using index_t = std::integral_constant<size_t, N>; // C++14
template<size_t M>
struct magic_switch_t {
template<class F, class...Args>
using R=std::result_of_t<F(index_t<0>, Args...)>;
template<class F, class...Args>
R<F, Args...> operator()(F&& f, size_t i, Args&&...args)const{
if (i >= M)
throw i; // make a better way to return an error
return invoke(std::make_index_sequence<M>{}, std::forward<F>(f), i, std::forward<Args>(args)...);
}
private:
template<size_t...Is, class F, class...Args>
R<F, Args...> invoke(std::index_sequence<Is...>, F&&f, size_t i, Args&&...args)const {
using pF=decltype(std::addressof(f));
using call_func = R<F, Args...>(*)(pF pf, Args&&...args);
static const call_func table[M]={
[](pF pf, Args&&...args)->R<F, Args...>{
return std::forward<F>(*pf)(index_t<Is>{}, std::forward<Args>(args)...);
}...
};
return table[i](std::addressof(f), std::forward<Args>(args)...);
}
};
magic_switch_t<N>{}( f, 3, blah1, blah2, etc )
will invoke f(index_t<3>{}, blah1, blah2, etc)
.
Some C++14 compilers will choke on the variardic pack expansion containing a lambda. It isn't essential, you can do a workaround, but the workaround is ugly.
The C++14 features are all optional: you can implement it all in C++11, but again, ugly.
The f
passed basically should be a function object (either a lambda taking auto
as the first argument, or a manual one). Passing a function name directly won't work well, because the above best works when the first argument becomes a compile-time value.
You can wrap a function template with a lambda or a function object to help.
For C++ 11, non-type template arguments are restricted to the following (§14.3.2/1):
A template-argument for a non-type, non-template template-parameter shall be one of:
- for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
- the name of a non-type template-parameter; or
- a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
- a constant expression that evaluates to a null pointer value (4.10); or
- a constant expression that evaluates to a null member pointer value (4.11); or
- a pointer to member expressed as described in 5.3.1.
In C++ 98 and 03, the list is even more restricted. Bottom line: what you're trying to do simply isn't allowed.
Template arguments must be compile-time constants aka "constant expressions" or constexpr
s for short. So there is no way to do is using templates.
You could use a dynamic-sized array and store its size in an int
.
Or simply use a vector
. Be sure to initialize its size in the constructor by passing the desired size to the vector's constructor!