简体   繁体   中英

Method of one class as callback from another

I will describe my problem the simplest as I can.

What is my issue:

I have frist class as a singleton:

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

And here is second class where I would like to have possibility to call setChannelA method from CTimer1 class as a setPwm method from 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
};

Here is registerPwmTimer method:

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

Then I have tried to assign pointer to this method as a following:

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)
    {

    }
}

I have error:

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

What is important:

I can't use std::function because this is some part of code in C++ for embedded device, so I need to save memory consumption. Is there any way that I will be able to achieve this effect? If ony one possibility to do this is ot use some std library please for answers too. Thanks for your help.

Your problem is that a function pointer necessarily has to point to a static function. When you invoke an instance function (a method) there is a hidden first argument, which is the object on which the function was invoked. (This hidden argument is available as this within the function's definition.)

Your CServo::registerPwmTimer() function signature is simply incompatible with invocation of a member function; function pointers alone do not provide a way to bind an argument to the pointer, so even if you could convey the member function pointer using a (free) function pointer type, the hidden this argument could not be determined when the function pointer was invoked.

To put it another way, it would fail for the same reason that trying CTimer1::setChannelA(0) would fail -- you want to invoke that method, but you haven't communicated which object on which to invoke it.

Change the signature of CServo::registerPwmTimer to accept an std::function object instead of a raw function pointer. std::function objects can be constructed from function pointers, but they can also be constructed from lambdas, and some standard library functions return function objects:

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

Now, you can use std::bind to create a new function that binds the object instance to the member function pointer:

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

Note that std::bind does not extend the lifetime of the object pointed to by timer1 . If the returned function is invoked after that object is destructed, the result is undefined behavior.


Another alternative would be to accept both an instance and a pointer to a member function. The problem with this approach is it requires using templates:

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

This isn't bad in itself, but what you'll wind up doing is creating a polymorphic wrapper class so that you can insert this into your callback list alongside other callbacks that don't share the same T . At that point, you're just recreating std::function , since std::function already serves the purpose of being a polymorphic wrapper around callable things.


To illustrate the mess of implementing a polymorphic callable wrapper yourself, here is a very light example. I will show the declarations of a set of these types, and link to an example implementation.

This is the base type, with a pure virtual operator() that serves as the invocation operation.

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

Now we have a type for function pointers (also works with pointer-to-functor):

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

And one for member functions -- oh, but const member functions and non- const member functions are not interchangeable, so we need an extra template parameter for that:

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

Plus we'll want some helper functions to allow the compiler to infer the template types. One for function pointers:

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

Two for member functions ( const and non- 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));
}

If you want to see it all in action, I made a "simple" and working example .

So... just use std::function . There's no reason to reinvent this stuff.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM