Passing a non-stationary member function as a parameter

I have many instances of a class X

and each contains a different class CB

(sometimes more than one CB

). The class CB

has a function pointer in it that will be called after some event (click, timer, etc.).

It would be so helpful if I could just pass the instantiated instance function to a local instance of the class CB

(pass a function in this case say()

) so that once the CB

function runs, it will modify things in its own instance.

Sample code example:

 #include <iostream>                                                                                                     

 class CB {                                                                                                
     void (*function)(void);                                                                                             

     void init(void (*function)(void)) {                                                                                 
         this->function = function;                                                                                      
     }                                                                                                                   
 };                                                                                                                      

class X {                                                                                                               
    private:                                                                                                            
        CB cb;                                                                                                          

    public:                                                                                                             
        void init() {                                                                                                   
            cb.init( &(this->say) ); //<- Here the problem                                                            
        }                                                                                                               

        void say() {                                                                                                    
            std::cout << "Hello World!" << std::endl;                                                                   
        }                                                                                                               
};                                                                                                                      

int main() {                                                                                                            
    X x;                                                                                                                
    x.init();                                                                                                           
} 

      

So my above example failed due to line 17 with error:

no known conversion for argument 1 from β€˜void (X::*)()’ to β€˜void (*)()’

      

Is it possible in some way to pass instances of a local function say()

to instances CB

? The function CB

cannot be changed and, unfortunately, will be used by other classes of a different type.

I read in other threads that this is most likely not possible unless the function say()

exists static

or exists outside of the class (which means it won't have access to instance variables).

Is it possible for this to work the way I do? If not for what would be the most common solutions (this sounds like a problem many people have encountered)?

+3


source to share


2 answers


Here's one way to get something even vaguely similar to this, but still work:

#include <iostream>
#include <functional>

class CB
{
public:
    std::function<void(void)> function;

    template <class F>
    CB(F function) : function(function) { }

    void operator()() { function(); }
};

class X
{
private:
    CB cb;

public:
    X() :cb([this] {say(); }) { }

    void say() { std::cout << "Hello World!\n";  }

    void operator()() { cb(); }
};

int main() {
    X x;
    x();
}

      

I rewrote CB a bit here. Some of them are incompatible with existing code, but that's because the existing design is just not a good idea. Now you're doing what is commonly known as two-step initialization, where you need to create an object and call it init()

before that object is actually ready to use. This is usually not approved.

I also changed CB to save std::function

, which can be used to store a pointer to a function, or pretty much anything that can be called like a function.



Then I added operator()

to actually call the Stored Functional Like Object.

As X

I made some changes, and the biggest - use a lambda expression to create an object that can be invoked as a function. This is passed to the embedded instance CB

at build time.

So the sequence is that the external code creates the object X

. This creates a built-in instance of the CB class, passing it a closure that calls X::say

. Then we call X::operator()

, which is calling CB::operator()

, which is calling X::say()

.

0


source


"Is it possible somehow to pass a local function to say()

instances of instances CB

?

If you're sure you want a function pointer, use a std::function

wrapper:

class CB {
    // void (*function)(void); Replace this with:
    std::function<void,void> fn_;
public:                                                                                  
    // void init(void (*function)(void)) { 
    //     this->function = function;
    // } Replace with a constructor
    CB(const std::function<void,void>& fn) : fn_(fn) {}
};

      

If you need to bind it to a specific instance of the class, use std::bind

:

class X {
     CB cb_;
public:
    // Again use a constructor with a member initializer list
    void X() : cb_(std::bind(&X::say, this) {}

    void say() {
        std::cout << "Hello World!" << std::endl;
    }
};

      

"The CB function cannot be changed and will unfortunately be used by other classes of a different type."
β€œI've read on other threads that this is most likely not possible unless the say () function is static or exists outside of the class (which means it won't have access to instance variables).



If so, you are a little lost. Since the signature of the callback function does not provide some sort of custom data parameter, it is not possible to provide a wrapper callback provider that contains a reference to a specific instance.

This callback function in your real code (I assume you simplified it in your question) does indeed have a signature void fn(void);

or provide some parameter that allows you to communicate with any data instance, for examplevoid fn(void* userData);


You can still provide a member static

function class X

to the function as a callback. If this function say()

doesn't change state X

(as in your example), changing it static

isn't really a problem (but I'm afraid not what you did here):

class CB {
    void (*fn_)(void);
public:
    CB(void (*fn)(void)) : fn_(fn) {}
};

class X {
     CB cb_;
public:
    // Again use a constructor with a member initializer list
    void X() : cb_(&X::say) {}

    static void say() {
 // ^^^^^^
        std::cout << "Hello World!" << std::endl;
    }
};

      

0


source







All Articles