问题
I am dealing with a 3rd party C++ library which makes extensive use of templates. That makes it difficult to create a C API for it to use it from my framework.
Abstracting the problem, suppose the library offers the function:
template <int i> void foo();
template <int i> void zoo(int v);
I want to create a C API with the function head:
extern "C" void c_foo(int i);
extern "C" void c_zoo(int i, int v);
An obvious implementation could be:
void c_foo(int i)
{
switch(i) {
case 1: foo<1>(); break;
case 2: foo<2>(); break;
case 3: foo<3>(); break;
default: break;
};
};
and do the same also for void zoo(int)
.
This works fine if the range of possible values for i
is small. If I want to handle all possible values of i
in [1,100], then it becomes exceedingly ugly to write code in this way, as there is lot of repetition.
Is there any more compact way to do that, i.e. writing less lines of codes? Perhaps using recursive preprocessor macros?
回答1:
You could use templates internally to generate the necessary code.
One way to do this is generate a dispatch table of 100 function pointers, then index it at runtime. c_foo
will generate a compile-time sequence of indices and call a helper:
extern "C" void c_foo(int i) {
c_foo_impl(std::make_integer_sequence<int,100>{}, i);
}
This helper will generate the dispatch table and carry out the call:
template <int... Is>
void c_foo_impl (std::integer_sequence<int,Is...>, int i) {
constexpr std::array<void(*)(), sizeof...(Is)> dispatch = { &foo<Is>... };
//assert or some other error handling for i > sizeof...(Is)
dispatch[i]();
}
Then you can do the same for zoo
:
extern "C" void c_zoo(int i, int v) {
c_zoo_impl(std::make_integer_sequence<int,100>{}, i, v);
}
template <int... Is>
void c_zoo_impl (std::integer_sequence<int,Is...>, int i, int v) {
constexpr std::array<void(*)(int), sizeof...(Is)> dispatch = { &zoo<Is>... };
//assert or some other error handling for i > sizeof...(Is)
dispatch[i](v);
}
If you find that you need this in a few places, you could abstract out some of the details, or use a library such as Petra, which provides a switch_table
to carry out this kind of mapping.
Live demo
回答2:
I guess the question is what is this template number? Is it something like an internal buffer size, or more like a command byte? One presumes the average C++ program would only generate a certain few argument values.
Perhaps the bast approach would be to enumerate some typical buffer sizes, or command names, and instantiate only those, so if the c code uses your enum as the parameter it will work, otherwise it (fails horribly) asserts.
It will be an annoyance to add new entries if they are justified, but you could write a script for it.
Another approach may be to generate mangled named stubs, i.e. generate a foo__1() foo__2() etc, perhaps using the boost extended preprocessor to help. Again the C programmer is then automatically restricted to the methods and ranges you have published at compile time.
来源:https://stackoverflow.com/questions/47788501/bridging-templates-with-runtime-arguments