0

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);
}
doufunao
  • 155
  • 8
  • This wouldn't work right anyway, since you are doing `UB` when you pass a `fobj` of type `Foo` to `print_ttt`, and thus using the `vtable` of `Foo` to call a function that is only in `Foo2`. – Mats Petersson Aug 06 '13 at 23:24
  • Is there any particular reason you can't just store a bunch of `Foo` (and derived) references, and call the same virtual function on each object? Maybe your example is too simple for what you REALLY want to do, but it looks like you should be able to do that. – Mats Petersson Aug 06 '13 at 23:30
  • you are right. But how can i make it work? I want Base class to run a set of registered derived class's functions, which take same parameters. – doufunao Aug 06 '13 at 23:36
  • Feels like an XY question, maybe if we step back a bit and you describe the original problem, rather than trying to work out your perceived solution, we can find a solution... – Mats Petersson Aug 06 '13 at 23:38
  • thank you. I revised my example. – doufunao Aug 06 '13 at 23:46
  • Right, so you CAN NOT do this with a virtual function, that's for sure - because that simply won't call the right function. In general, I think if you are using function pointers in code that also uses inheritance [that are `virtual`?], then something is not quite right. The whole point of inheritance and virtual functions is to avoid function pointers, and move the responsibility of knowing what functions to run down a level - or something like that. I'll write an answer to that effect, if you don't like it, please say so... – Mats Petersson Aug 06 '13 at 23:54
  • you can see I'm trying to keep a set of function pointers (I guess a pointer to the object is needed as well) defined in derived classes so that I can run all registered callback functions by calling `base_ptr->runCallbacks()`. the number of callbacks could be tens. – doufunao Aug 07 '13 at 00:08
  • I'm pretty sure you are trying to produce some sort of event handling. I'm also pretty sure what you REALLY want isn't a vector of callback functions to different types of objects. It's just not going to work that way. You need to find a different way of solving the problem. – Mats Petersson Aug 07 '13 at 00:13

1 Answers1

1

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);
    }
}
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • thanks for the code. but can you do it by using function object or ptr? due to some impl details, I can't do it in your way, because I do need to use func ptr. reason 1: my real problem is not running **all** callbacks. the base/caller need to pick the right one based on the input. (i revised example again. sorry:) ) reason 2: i want to know if function object or ptr can solve my problem. I believe it can but I just haven't figure that out. – doufunao Aug 07 '13 at 00:28
  • I will get back to you in the morning, it's 1.30 at night here, and I'm not quite awake enough to think through all the different consequences - but I think what you need is a an interface class that you then derive your actual object from, and do the implementation that way - it is still wrong (in my opinion) to build a system based on function pointers in C++ (as a general rule, exceptions do exist, but I don't think this is one of those). – Mats Petersson Aug 07 '13 at 00:35
  • Mats, thank you for your answers! Could you explain why using function pointers is wrong? We use it a lot in our system and so far I am not seeing any significant drawbacks. I'd love to know your opinion. Thanks! – doufunao Aug 07 '13 at 14:39
  • It's not the "C++" solution. If you've ever programmed Python, you may be familiar with a concept called "Pythonic" - the "right" Python way to solve a problem. Function pointers are fine for certain things, but this doesn't seem like one of those times. Obviously, in C++ even more so than in Python, there are LOTS of different ways to achieve the same thing. – Mats Petersson Aug 07 '13 at 15:00
  • you are right I'm doing event handling. One reason I can't use virtual function as you shown is that I thought my event types were some template classes. but that was solved with a different approach. I revised my code, applied your idea in your first example and removed all function ptrs. the new code passed all tests. so i believe it's working properly. thank you! – doufunao Aug 07 '13 at 18:54