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

时间:2017-03-14 20:41:01

标签: c++ avr

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

我的问题是什么:

我将frist课作为单身人士:

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;
};

这是第二课,我希望有可能从setChannelA类调用CTimer1方法作为setPwm类的CServo方法:

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 ++中嵌入式设备代码的一部分,所以我需要节省内存消耗。有什么方法可以达到这个效果吗?如果有一种可能性,那么请使用一些标准库来获取答案。谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

您的问题是函数指针必须指向静态函数。当您调用实例函数(方法)时,会有一个隐藏的第一个参数,该参数是调用该函数的对象。 (此隐藏参数在函数定义中以this形式提供。)

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

换句话说,它会失败的原因与尝试CTimer1::setChannelA(0)失败的原因相同 - 你想要调用那个方法,但你还没有传达哪个对象可以调用它。

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

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

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

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

请注意,std::bind 不会延长timer1指向的对象的生命周期。如果在该对象被销毁之后调用返回的函数,则结果是未定义的行为。

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

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

这本身并不错,但你最后要做的是创建一个多态包装类,这样你就可以将它插入你的回调列表以及不共享同一个T的其他回调。那时,你只是在重新创建std::function ,因为std::function已经成为围绕可调用事物的多态包装的目的。

为了说明自己实现多态可调用包装器的混乱,这是一个非常轻松的例子。我将展示一组这些类型的声明,并链接到一个示例实现。

这是基本类型,其中纯虚拟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没有理由重新发明这些东西。