一个 class 作为另一个回调的方法

Method of one class as callback from another

我会尽可能简单地描述我的问题。

我的问题是什么:

我第一次 class 单身:

class CTimer1
{
public:
    static CTimer1 * getInstance();       //This gives me pointer to instance
    void setChannelA(uint8_t compareValue);
private:
    //Cnstructors
    CTimer1();  //Prevent consttuction but allow in getInstance
    CTimer1(const CTimer1&);    //Prevent construction by copying
    CTimer1& operator=(const CTimer1&); //Prevent assigment
    ~CTimer1();                 //Prevent unwanted destruction
    static CTimer1 * timerInstance;
    static bool isCreated;
};

这是第二个 class,我希望可以从 CTimer1 class 调用 setChannelA 方法作为 setPwm 方法 CServo class:

class CServo {
public:
    CServo();
    ~CServo();

public:
    //public methods
    void registerPwmTimer(void (*callback)(uint8_t u8_buffer));

    void (*setPwm)(uint8_t u8_buffer);   //As this method I would like to call setChannelA from CTimer1 class
};

这里是registerPwmTimer方法:

void CServo::registerPwmTimer(void (*callback)(uint8_t u8_buffer))
{
    setPwm = callback;
}

然后我尝试按如下方式将指针分配给此方法:

int main()
{   
    CTimer1 * timer1 = CTimer1::getInstance();
    CServo servo1();
    servo1.registerPwmTimer(timer1->setChannelA);
    servo1.setPwm(10);       //This is example how I want to call setChannelA method

    while(1)
    {

    }
}

我有错误:

error: no matching function for call to 'CServo::registerPwmTimer(<unresolved overloaded function type>)'

重要的是:

我不能使用 std::function 因为这是嵌入式设备的 C++ 代码的一部分,所以我需要节省内存消耗。有什么办法可以达到这种效果吗?如果这样做的唯一一种可能性是不使用某些标准库,请也寻求答案。感谢您的帮助。

您的问题是函数指针必须指向静态函数。当你调用一个实例函数(一个方法)时,有一个隐藏的第一个参数,它是调用该函数的对象。 (此隐藏参数在函数定义中作为 this 可用。)

您的 CServo::registerPwmTimer() 函数签名与成员函数的调用不兼容;单独的函数指针不提供将参数绑定到指针的方法,因此即使您可以使用(自由)函数指针类型传递成员函数指针,隐藏的 this 参数也无法在函数时确定指针被调用。

换句话说,它失败的原因与尝试 CTimer1::setChannelA(0) 失败的原因相同——您想要调用该方法,但您还没有传达在哪个对象上调用它。

更改 CServo::registerPwmTimer 的签名以接受 std::function 对象而不是原始函数指针。 std::function 对象可以从函数指针构造,但它们也可以从 lambdas 构造,一些标准库函数 return 函数对象:

void registerPwmTimer(std::function<void(uint8_t)>);

现在,您可以使用std::bind创建一个将对象实例绑定到成员函数指针的新函数:

servo1.registerPwmTimer(std::bind(&CTimer1::setChannelA, timer1));

请注意 std::bind 不会 延长 timer1 指向的对象的生命周期。如果在对象被析构后调用 returned 函数,结果是未定义的行为。


另一种选择是同时接受实例和指向成员函数的指针。这种方法的问题是它需要使用模板:

template <typename T>
void registerPwmTimer(void (T::*)(uint8_t), T&);

这本身并不坏,但您最终要做的是创建一个多态包装器 class,这样您就可以将它与其他不共享的回调一起插入到您的回调列表中同样 T。到那时,你只是在重新创建 std::function,因为 std::function 已经起到了围绕可调用事物进行多态包装的目的。


为了说明自己实现多态可调用包装器的麻烦,这里有一个非常简单的示例。我将展示一组这些类型的声明,并 link 示例实现。

这是基本类型,具有纯虚拟operator()作为调用操作。

class poly_callable
{
public:
    virtual void operator()(int) const = 0;
};

现在我们有了函数指针的类型(也适用于函子指针):

template <typename T>
class fn_poly_callable : public poly_callable
{
public:
    typedef T fn_type;

    fn_poly_callable(T);
    virtual void operator()(int) const;

private:
    T fn;
};

还有一个用于成员函数 -- 哦,但是 const 成员函数和非 const 成员函数不可互换,因此我们需要一个额外的模板参数:

template <typename T, typename M = void (T::*)(int)>
class member_poly_callable : public poly_callable
{
public:
    typedef T object_type;
    typedef M member_fn_type;

    member_poly_callable(member_fn_type, object_type&);
    virtual void operator()(int) const;

private:
    member_fn_type mfn;
    object_type& target;
};

此外,我们还需要一些辅助函数来允许编译器推断模板类型。一个用于函数指针:

template <typename T>
std::unique_ptr<poly_callable> make_poly_callable(T fn)
{
    return std::unique_ptr<poly_callable>(new fn_poly_callable<T>(fn));
}

两个成员函数(const和非const):

template <typename T>
std::unique_ptr<poly_callable> make_poly_callable(void (T::*mfn)(int), T& target)
{
    return std::unique_ptr<poly_callable>(new member_poly_callable<T>(mfn, target));
}

template <typename T>
std::unique_ptr<poly_callable> make_poly_callable(void (T::*mfn)(int) const, T& target)
{
    return std::unique_ptr<poly_callable>(new member_poly_callable<T, void (T::*)(int) const>(mfn, target));
}

如果你想看到这一切,我做了一个 "simple" and working example

所以...只需使用 std::function没有理由重新发明这些东西。