生成指向回调函数的常量指针数组

Generate const array of pointers to callback functions

我想生成一个包含 N 个回调指针的数组,这样我就不必显式键入它们(这里的 LOC 不是问题)。 我使用 C++17.

这是我的:

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]();

这是我想要的:

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

我尝试了各种方法,但我一直 运行 研究常量参数的问题,即使它们来自 constexpr 函数或可变参数模板。

如果我试试这个:

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]();
}

我收到以下错误:

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

如果我将 i 设为静态并省略捕获,它只使用 i 的最后一个值:

callback 2
callback 2

这对我来说很奇怪,因为捕获应该在构造时完成。 lambda 是在循环退出后构造的吗?

至于目的。我想应用这种技术为微控制器生成中断处理程序。我可以直接把函数指针放在中断向量table中。这些函数没有参数,而且我也不知道检测调用处理程序的中断源的简洁方法。我可以为每个中断编写一个处理程序,但我不喜欢将这段代码重复 6 次:

void handler0()
{
  do_something(0);
}

使用模板将其键入为 lambda and/or 使其更清晰一些,但我仍然需要键入 N 次内容。如果 N 发生变化,我必须更改多行代码。这不优雅。

题外话建议:尽可能不要使用 C-styles 数组,而是使用 C++ std::array.

例如:下面一行

const auto callbacks = generate_callbacks<N>(); 

如果您希望 callbacks 是一个 C-style 数组(函数不能 return 该类型)则无法工作,但在 generate_callback() return一个std::array<Callback, N>实例。

题外话建议结束。

在这种特殊情况下,鉴于 N 是一个 constexpr 值,我建议使用模板 meta-programming.

所以我建议使用以下 generate_callbacks() 函数,它只创建一个从零到 N-1 的模板值序列并调用辅助函数

template <std::size_t N>
auto generate_callbacks ()
{ return gc_helper(std::make_index_sequence<N>{}); }

和一个简单的辅助函数,它使用模板值并创建回调 lambda 而不捕获它们(因此仍然可以转换为函数指针)

template <std::size_t ... Is>
std::array<Callback, sizeof...(Is)> gc_helper (std::index_sequence<Is...>)
{ return {{ []{ auto i = Is; std::cout<<"callback " << i << "\n"; }... }}; }

如果您可以使用 C++20,使用模板 lambda,您可以避免使用外部 gc_helper() 函数,并在 generate_callbacks() 内部进行如下操作

template <std::size_t N>
auto generate_callbacks ()
{
  return []<std::size_t ... Is>(std::index_sequence<Is...>)
    -> std::array<Callback, N>
  { return {{ []{ std::cout<<"callback " << Is << "\n"; }... }}; }
  (std::make_index_sequence<N>{});
}

以下为完整编译C++17 C++14范例

#include <iostream>
#include <utility>
#include <array>

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

template <std::size_t ... Is>
std::array<Callback, sizeof...(Is)> gc_helper (std::index_sequence<Is...>)
{ return {{ []{ auto i = Is; std::cout<<"callback " << i << "\n"; }... }}; }

template <std::size_t N>
auto generate_callbacks ()
{ return gc_helper(std::make_index_sequence<N>{}); }

int main()
{
  const auto callbacks = generate_callbacks<N>();

  for ( auto ui = 0 ; ui < N ; ++ui )
    callbacks[ui]();
}

以下代码在 C++17 模式下的 gcc 和 clang 中都可以正常编译。它使用一些简单的模板元编程来生成回调序列。

#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]();
    }
}