Search code examples
c++c++11stlstd-functionstdbind

in base class, how to define a container to contain function obj which can be any func of derived class?


I want to define a container in the base class, which contains function obj or anything that can make my purpose happen. These function obj can call derived classes' functions. they all take same parameters.

#include <vector>
#include <functional>
#include <iostream>


class Foo {
    Foo() {}
    virtual ~Foo(){}

    virtual void init()
    { registerCallback(0, &Foo::print_ori ); }
    void print_ori(int i) const { std::cout << i << '\n'; }

    void registerCallback(int key, ??? cb ) // NOT SURE HOW TO DEFINE THIS
    {
        callbacks[key] = cb;
    }

    void runCallbacks(int key, int n)
    {
        auto i = callbacks.find(key);
        if (i != callbacks.end()) {
            (*i)(*this, n);
        }
    }

    std::map<int, std::function<void(const Foo&, int) > > callbacks; // obviously, it's wrong. how to fix it?
};
struct Foo2 : public Foo {
    Foo2(int num) : Foo(num) {}
    virtual void init()
    {
        Foo::init();
        registerCallback(11, &Foo2::print1 );
        registerCallback(12, &Foo2::print2 );
    }
    void print1(int i) const { std::cout << " - Foo2.p1 - " << i << endl; }
    void print2(int i) const { std::cout << " - Foo2.p2 - " << i << endl; }
};



int main()
{
    Foo* obj = new Foo2();
    obj->init();
    obj->runCallbacks(12, 456);
}

Solution

  • Here's a way to achieve what your code looks like it's trying to do, without using function pointers:

    class Foo {
        Foo() {}
        virtual ~Foo(){}
    
        void print_ori(int i) const { std::cout << i << '\n'; }
    
        virtual void do_runCallbacks(int v)
        {
        }
    
        void runCallbacks()
        {
            print_ori(3)
            do_runCallBacks(3);
        }
    
    };
    struct Foo2 : public Foo {
        Foo2(int num) : Foo(num) {}
    
        void do_runcallbacks(int v)
        {
           print1(v);
           print2(v);
        }
        void print1(int i) const { std::cout << " - Foo2.p1 - " << i << endl; }
        void print2(int i) const { std::cout << " - Foo2.p2 - " << i << endl; }
    };
    
    
    
    int main()
    {
        Foo* obj = new Foo2();
        obj->runCallbacks();
    }
    

    Now, there may well be reasons to do this completely differently, but I don't see why you should need both virtual functions and inheritance, AND function objects/function pointers. That seems quite wrong to me ("smells bad")

    Edit:

    Here's something I came up with, that solves the type of problem you describe after edits of the original question.

    #include <iostream>
    #include <map>
    
    using namespace std;
    
    class event_interface
    {
    public:
        virtual void action(int n) = 0;
    };
    
    class event_manager
    {
    public:
        event_manager(int n) : num(n) {}
        void register_event(int key, event_interface *eh) 
        { 
            handlers[key] = eh; 
        }
        void callback(int key)
        {
            auto h = handlers.find(key);
            if (h != handlers.end())
            {
            h->second->action(num);
            }
        }
    private:
        map<int, event_interface *> handlers;
        int num;
    };
    
    
    class handler1 : public event_interface
    {
    public:
        void action(int n) { cout << "in handler1::action. n=" << n << endl; }
    };
    
    class handler2 : public event_interface
    {
    public:
        handler2(int n) : data(n) {}
        void action(int n) 
        { 
            cout << "in handler2::action. n=" << n 
             << " data = " << data << endl; 
        }
    private:
        int data;
    };
    
    class multihandler 
    {
    private:
        class handler3: public event_interface
        {
        public:
        void action(int n) { cout << "in handler3::action. n=" << n << endl; }
        };
    
        class handler4: public event_interface
        {
        public:
        handler4(multihandler *m) : mh(m) {}
        void action(int n) 
            { 
            cout << "in handler4::action. n=" << n 
                 << " data = " << mh->data << endl; 
            }
        private:
        multihandler* mh;
        };
    
    public:
        multihandler(event_manager& em) : h4(this)
        {
            em.register_event(62, &h3);
            em.register_event(63, &h4);
            data = 42;
        }
    
    private:
        handler3 h3;
        handler4 h4;
        int data;
    };
    
    
    int main()
    {
        event_manager mgr(3);
        handler1 h1;
        handler2 h2(77);
    
        multihandler mh(mgr);
    
        mgr.register_event(12, &h1);
        mgr.register_event(13, &h2);
    
        int evts[] = { 12, 63, 62, 13, 18 };
    
        for(auto i : evts)
        {
        cout << "Event: " << i << endl;
        mgr.callback(i);
        }
    }