2

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?

Fabio
  • 2,105
  • 16
  • 26
  • _"Perhaps using recursive preprocessor macros?"_ There's a number of libraries offering helper macros to do such. E.g. the boost preprocessor lib. – user0042 Dec 13 '17 at 08:27

2 Answers2

5

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

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • I like the idea, but I wonder if the dispatch table is regenerated at every call. I think I will try a slight variation, embedding the pointer vector in a struct, so that that I can then create a global instance of that struct and use that for dispatching. – Fabio Dec 13 '17 at 09:17
  • You could make `dispatch` `constexpr` to avoid regeneration. – TartanLlama Dec 13 '17 at 09:25
  • The assembler generated by VS2015 shows that even with `constexpr` the array is re-initialized at every call. The problem goes away using `static`. – Fabio Dec 14 '17 at 04:24
  • Just posted a related question here: https://stackoverflow.com/questions/47835596/use-the-signature-of-a-generic-function-dependent-on-a-non-type-template-argumen – Fabio Dec 15 '17 at 15:45
0

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.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27