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 just use a higher-order looping macro that passes the block implementation to a generic loop expander:
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__
#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)
#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
//...etc
#define INSTANTIATE_DEPENDING(L, C) M_FOR_EACH(C, M_ID L)
//...
#define CASE_BLOCK(n) case n: { Wrapper<n> w; w.foo(); break; }
INSTANTIATE_DEPENDING((1, 2, 3), CASE_BLOCK)
#undef CASE_BLOCK //if you like, not essential to the concept
Not a lot to say about that: the loop repeats the block for the length of the passed list, passing the items in the list to the macro it is to expand. So you put your implementation in that macro (and #undef
it if you want it to be local).
More elegantly (letting you nest the parameterized code to expand inside the expression where it belongs, instead of a second definition), you could use the rather high-end Order metaprogramming library:
#include <order/interpreter.h>
ORDER_PP( // runs Order code
8for_each_in_range(8fn(8I,
8print( (case) 8I (: { )
(Wrapper<) 8I (> w; w.foo(); break; }) )),
1, 4)
)
(Use 8for-each
instead of 8for_each_in_range
for non-contiguous lists. Order's got full functional programming semantics so such things are minor issues.)
just use macros!
template<unsigned A>
struct Wrapper {
int content[A];
void foo() { };
};
#define WRAPPER_SWITCH_CASE(i) case i: Wrapper<i>().foo(); break;
int main(int argc, char *argv[])
{
std::string arg = argv[1];
int arg_int = std::stoi(arg);
switch (arg_int) {
WRAPPER_SWITCH_CASE(1)
WRAPPER_SWITCH_CASE(2)
WRAPPER_SWITCH_CASE(3)
default: return 1;
};
return 0;
}
(live example)
But as you know, macros are harmful; I think Wrapper
should be allocate content
at runtime, not template.
a short prof of concept application using a recursive generator for the Wrappers:
#include <iostream>
#include <vector>
struct FooProvider
{
virtual void foo() = 0;
};
template<unsigned A>
struct Wrapper : public FooProvider {
Wrapper() {std::cout << A << std::endl;}
int content[A];
virtual void foo() { std::cout << "call:" << A << std::endl;};
};
static std::vector<FooProvider*> providers;
template <unsigned CTR>
struct Instantiator
{
Instantiator()
{
providers.insert(providers.begin(), new Wrapper<CTR>);
Instantiator<CTR - 1>();
}
};
template <>
struct Instantiator<0>
{
Instantiator() {}
};
int main()
{
Instantiator<100>();
providers[4]->foo();
// do not forget to delete the providers
}
Inspired by Kerrek SB's answer with variadic templates, here is a solution which can easily be extended to multiple parameters of any type:
template <int param1_>
struct Params
{
const static int kParam1 = param1_;
// Add other parameters here if needed
};
// Default case: list is empty
template <typename T>
void handle_cases(const T param1) { }
// Regular case: list is not-empty
template <typename T, typename head, typename ...tail>
void handle_cases(const T param1)
{
if (head::kParam1 == param1)
{
Wrapper<head::kParam1> w;
w.foo();
}
else
{
handle_cases<T, tail...>(param1);
}
}
Note that typename T
is just an example of an additional template parameter which is not part of the head/tail list.
And here is how to use it:
int main(int argc, char * argv[])
{
if (argc != 2) { return EXIT_FAILURE; }
handle_cases<int, Params<1>, Params<3>, Params<4>, Params<9>, Params<11>>(std::stoi(argv[1]));
}