如何将C ++对象的成员函数用作C样式的回调?

时间:2018-10-15 15:54:29

标签: c++

我需要使用对象实例函数作为不带参数的C样式回调,它不能是静态的,因为我需要访问实例变量,并且无法使用回调修改函数。

调用方函数的签名为:

void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode);

A,我无法使用捕获列表,因为它是仅C风格的回调,因此以下操作无效:

attachInterrupt(pin, [this]() { tracker(); }, mode);

我保留了指向对象实例的静态指针数组,所以我尝试了以下方法:

static Actuator* actuator;
actuator = instance[index];
attachInterrupt(pin, [] { actuator->tracker(); }, mode);

问题是所有lambda函数都共享对执行器中存储的指针的相同引用,但是我需要每个lambda函数都使用存储在instance [index]中的特定指针。

我要让它正常工作的唯一方法是使用switch语句,但是我想尽可能避免硬编码。

switch (index) {
    case 0:
        ::attachInterrupt(pin, [] { instance[0]->tracker(); }, mode);
        break;
    case 1:
        ::attachInterrupt(pin, [] { instance[1]->tracker(); }, mode);
        break;
    ...
}

必须有一种方法,可以在不使用switch语句的情况下将对数组中特定静态元素的引用传递给lambda函数,我该如何实现呢?

2 个答案:

答案 0 :(得分:2)

如果您知道要在编译时注册的回调数,则可以将回调创建为模板函数:

template <size_t i>
static void callback()
{
    instance[i]->tracker();
}

然后,您可以使用std::make_index_sequence和可变参数模板的解包语法来一次注册所有内容:

template <size_t... ids>
void register_all_callbacks_impl(std::index_sequence<ids...>)
{
    // the (f(), 0) syntax calls a void function f but returns an int 0
    // with this trick you can leverage the unpacking syntax
    // to call void functions in an array initialization 
    int dummy[] = { (attachInterrupt(pin, &callback<ids>, mode), 0)... };
    (void)dummy; // omit "unused variable" warnings
}

constexpr size_t COUNT = ...;

void register_all_callbacks()
{
    register_all_callbacks_impl(std::make_index_sequence<COUNT>());
}

您现在要做的就是初始化大小为instance的{​​{1}}数组并调用COUNT

注意:register_all_callbacks()需要C ++ 14。

答案 1 :(得分:0)

我可能会尝试

template<uint32_t pin, uint32_t mode> class callback
{
public:
    static void attach() { ::attachInterrupt(pin, &callback::tracker, mode); }

private:
    // Make specialization for each combination
    static void tracker();
}

然后注册回调,您可以这样做:

callback<5, 3>::attach();
callback<7, 2>::attach();

如果模式是一个依赖于引脚号的常数,那么您将使用回调类的静态成员,然后为每种类类型初始化一个特定的值。

const uint32_t callback<7>::mode_for_this_pin = 3; // maybe constexpr instead?

但是由于该问题没有太多细节,因此很难给出一个好的解决方案。

我们假设调用回调函数时不可能知道该引脚,因此客户端将特定功能与每个引脚相关联。

或者,您也可以向该类中添加一个static std::function<void()> mfn成员,然后像这样初始化它:

callback<7, 3>::mfn = [] { your code here };

如果要执行的回调取决于某些条件,这可能很有用。

您的问题没有通用答案,因为该语言不直接支持该答案。最佳解决方案取决于问题中未记录的多种因素。

很显然,如果特定引脚的功能始终相同,则可以轻松使用功能pin3_callbackpin3_mode2_callback并在实现中做任何适当的事情。

应该在何时进行初始化以及是否需要清理的问题中未指定的其他要点。

我知道在Windows下,过去无法为回调提供用户数据的情况下,过去曾使用一些汇编代码来生成唯一函数。