Question

I've managed to write a template class to work like a callback, learned from the accepted answer of this question How to define a general member function pointer.

I wish to have a map of string keys and callback values so that I can invoke the proper callback that matches a string. This would be fine but I need the map to support callbacks from different classes. Right now it can only work with one class. It can be any class because of the template but only a collection of callbacks from the same class.

class Apple {
public:
    void Red () {
        cout << "aa";
    }
};

class Orange {
public:
    void Blue () {
        cout << "bb";
    }
};

template <typename T>
class Callback {
    T *obj;
    void (T::*ptr) (void);

public:
    Callback (T *obj, void (T::*ptr) (void)) : obj (obj), ptr (ptr) {

    }

    void Call () {
        (obj->*ptr) ();
    }
};

I can use this like so

Apple apple;
Orange orange;
Callback <Apple> callA (&apple, &Apple::Red);
Callback <Orange> callB (&orange, &Orange::Blue);
callA.call ();
callB.call ();

std::map <std::string, Callback <Apple>> appleCallbacks;

I want to be able to do this

std::map <std::string, Callback <anything>> anyCallbacks;

I plan to use this with a bunch of classes that share the same base class and have functions are identical to each other in definition except for the name and which class it belongs to.

class Base { };

class ChildA : public Base {
public:
    void Talk ();
}

class ChildB : public Base {
public:
    void Walk ();
}

So if this worked I would be able to put both Talk () and Walk () into the map.

Is this possible at all or is my point of view flawed to begin with?

Was it helpful?

Solution

Madness lies, this way: Don't bind callbacks to specific classes. Instead, use a std::function<Signature> object and create suitable function objects: when you need to operate on different classes, you also need to operate on objects of different types. Using a std::function<...> should do the trick, e.g.:

std::map<std::string, std::function<void()>> operations;
operations["talk"] = std::bind(&ChildA::Talk, ChildA());
operations["walk"] = std::bind(&ChildB::Walk, ChildB());
operations["talk"]();

OTHER TIPS

There's no need for inheritance here. Just use std::function to store the member function pointers and std::bind to bind together the member function pointer and the object instance.

#include <functional>
#include <map>
#include <string>
#include <iostream>

struct Apple {
    void Red () {
        std::cout << "aa\n";
    }
};

struct Orange {
    void Blue () {
        std::cout << "bb\n";
    }
};

int main()
{
    std::map<std::string, std::function<void()>> m;
    Apple a;
    Orange o;

    m["apple"] = std::bind(&Apple::Red, a);
    m["orange"] = std::bind(&Orange::Blue, o);

    m["apple"]();
    m["orange"]();
}

Output:

aa
bb
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top