Search code examples
c++templatestemplate-meta-programming

Template generating a hundred C callback functions, without slow compilation


I need to pass order of a hundred callback functions to a C library. All my callbacks will be very similar, except that they need to know which "key" it was registered for. The C API doesn't allow an context to be passed back.

In other words this is the API:

void register_for_event(int event_type, void(*callback)());

I came up with this template metaprogramming:

template<int event_type>
void event_handler() {
  std::cout << "Event: " << event_type << std::endl;
}

template <int n = 100>
void register_event_handlers() {
  register_for_event(n, event_handler<n>);
  register_event_handlers<n-1>();
}
template<> void register_event_handlers<0>(){}

But is there a way to write this:

  • easier to read
  • faster to compile (the above adds several seconds to building the file, with gcc)

Any version of C++, up to C++20, would work for me.

I'm trying to avoid macros, though it's not obvious to me how macros would even help here.

I could use code generation from shell script. I may end up doing this if I can't find a better C++-native way. But I'm also interested in what other native C++ ways are possible.


Solution

  • Here is a C++17 answer:

    template <std::size_t... I>
    void register_via_pack(std::index_sequence<I...>) {
        ( (register_for_event(I, event_handler<I>)),... );
    }
    
    register_via_pack(std::make_index_sequence<100>{});
    

    It expands a template parameter pack using a C++17 fold expression. Not sure if it compiles faster in your scenario, but it is a good 4 times faster here under g++ -O3. Even under -Og it produces unrolled code instead of a 100-deep recursion.

    You can use the trick here to do the same under C++11/C++14.

    You can get rid of the extra function by using an immediately-invoked variadic templated lambda function, but it will not be more readable ;)