'Generate const array of pointers to callback functions

I'd like to generate an array of N pointers to callbacks so I don't have to type them explicitly (LOC is not the issue here). I use C++17.

Here is what I have:

using Callback = void(*)();
auto constexpr N = 2;

const Callback callbacks[N] = {
 [](){ auto i = 0; std::cout<<"callback " << i << "\n";},
 [](){ auto i = 1; std::cout<<"callback " << i << "\n";}
};

callbacks[0]();
callbacks[N-1]();

Here is what I want:

const auto callbacks = generate_callbacks<N>();  // or generate_callbacks(N)
callbacks[i](); // cout<<"callback " << i << "\n";

I tried various ways, but I keep running into the problems with constant parameters even when they are from a constexpr function or variadic template.

If I try this:

Callback callbacks[N] = { };

for(int i=0;i<N;++i)
{
callbacks[i] = [i](){ std::cout<<"callback " << i << "\n";};
}

for(int i=0;i<N;++i)
{
callbacks[i]();
}

I get the following error:

main.cpp:91:66: error: cannot convert ‘main()::’ to ‘Callback {aka void (*)()}’ in assignment
        callbacks[i] = [i](){ std::cout<<"callback " << i << "\n";};

If I make i static and leave out the capture it only uses the last value of i:

callback 2
callback 2

This is odd to me as capturing should be done at construction. Are the lambdas constructed after the loop exits?

As for the purpose. I want to apply this technique to generating interrupt handlers for microcontrollers. I can put the function pointers in the interrupt vector table directly. These functions have no parameters and I don't know a clean way to detect which interrupt source called the handler. I can write a handler for each interrupt, but I don't like repeating this code 6 times:

void handler0()
{
  do_something(0);
}

Typing it as a lambda and/or using a template makes it a little cleaner, but I still have to type something N times. And if N changes I have to change multiple lines of code. This is not elegant.



Solution 1:[1]

The following compiles fine in both gcc and clang in C++17 mode. It uses some simple template metaprogramming to generate the sequence of callbacks.

#include <array>
#include <iostream>

using cb = void (*)();

template<int N>
inline auto fun()
{
    std::cout << "callback: " << N << '\n';
}

template<int N>
void init(cb * arr)
{
    arr[N] = &fun<N>;
    init<N-1>(arr);
}

template<>
void init<0>(cb * arr)
{
    arr[0] = &fun<0>;
}

template<int N>
struct callbacks
{
    callbacks()
    {
        init<N>(cbs.data());
    }
    
    std::array<cb, N> cbs;
};

int main()
{
    auto foo = callbacks<4>();
    for (auto x = 0; x < 4; ++x)
    {
        foo.cbs[x]();
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Krzysiek Karbowiak