Search code examples
c++stdbind

Passing a callable that may change to an instance of a class


I have a set of callables which I'd like to associate to instances of a particular class Node. Each instance of Node needs only hold one callable, and may call it from a member function. Suppose A, B, and C are callables, which may have different function signatures:

Node n1,n2,n3;
n1.associate(A);
n2.associate(B, 42);
n3.associate(C, "foo");

//...

n1.mem_fn(); //will call A at some point inside some_other_fn()
n2.mem_fn(); //will call B, passing 42 as argument

//...

n1.associate(C, "bar");
n1.mem_fn(); //now C will replace A, and be called from n1::mem_fn() with arg "bar"

How can I accomplish this? The return type of the callables may be different.


Solution

  • No need to use std::bind anymore. Lambdas are very simple to use to generate on the fly a callable object which also can store any kind of data as you like.

    class Node 
    {
        private:
            std::function<void(void)> fn;
    
        public:
            template <typename F, typename ... PARMS>
            void associate( F f, PARMS ... parms )
            {
                // Generate a lambda which captures all given parms
                // and if executed, the lambda calls f with all
                // the captured parms. This on the fly generated 
                // lambda will be stored in std::function 
                fn = [f, parms...](){ f( parms...); };
            }
    
            void mem_fn()
            {
                // Here we simply call our lambda which will call 
                // the stored function with the also stored parms
                fn();
            }
    };
    
    void A() { std::cout << "A" << std::endl; }
    void B( int i ) { std::cout << "B " << i << std::endl; }
    void C( const std::string& s ) { std::cout << "C " << s << std::endl; }
    
    // I use exact copy of your code as copy here:
    int main()
    {
        Node n1,n2,n3;
        n1.associate(A);
        n2.associate(B, 42);
        n3.associate(C, "foo");
    
        n1.mem_fn(); //will call A at some point inside some_other_fn()
        n2.mem_fn(); //will call B, passing 42 as argument
    
        //...
    
        n1.associate(C, "bar"); //now C will replace A, and be called from n1::mem_fn() with arg "bar"
        n1.mem_fn();
    }