0

I was trying to work on the below code but the program crashes:

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

using namespace std;

typedef void (*callBackMethod)(string);
class CTest
{
private:
    map<string, callBackMethod> mapMethod;

    void testMethod(string msg)
    {
        cout << msg << endl;
    }
public:
    CTest()
    {
        addFunction("AA", (callBackMethod) &CTest::testMethod);
    }
    void addFunction(string funName, callBackMethod methodName)
    {
        mapMethod[funName] = methodName;
    }
    callBackMethod getMethod(string funName)
    {
        auto fun = mapMethod.find(funName);
        if(fun == mapMethod.end()) { return nullptr; }
        return fun->second;
    }
    void runFunction(string funName)
    {
        getMethod(funName)("test");
    }

};

int main()
{
    CTest test;
    test.runFunction("AA");

    return 0;
}

I have a requirement where I need to pass private methods to a map. The program compiles with warning:

converting from 'void (CTest::*)(std::__cxx11::string) {aka void (CTest::*)(std::__cxx11::basic_string<char>)}' to 'callBackMethod {aka void (*)(std::__cxx11::basic_string<char>)}'

and when I execute this, it crashes.

When I move the callback method outside of the class it works. My requirement is to make the program flow this was (hide the methods from external call which needs to be added to a map).

Looking forward to your comments.

programmer
  • 582
  • 2
  • 4
  • 16

2 Answers2

0

Since you want to use member variables you need to specify the signature differently in your typedef:

In C++ Builder the following can be done:

typedef void(__closure *callBackMethod)(string);

If you do that, I do suggest that you keep a smart pointer to the object that the member belongs to so that you can check if the object is still valid before calling the function otherwise it will crash the application.

The __closure keyword is a C++ Builder extension to work around the requirement to use fully qualified member names source

To handle both global and member functions we have the following:

typedef void(__closure *callBackMethodMember)(string);
typedef void (*callBackMethodGlobal)(string);

/* And then on 2 overloaded functions */
void addFunction(string funName, callBackMethodMember methodName) {}
void addFunction(string funName, callBackMethodGlobal methodName) {}
CJCombrink
  • 3,738
  • 1
  • 22
  • 38
0

If you need to point to both CTest member functions and free functions, then you can use std::function<void(std::string)>.

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

using namespace std;

using callBackFunction = std::function<void(string)>;

void testFunction(string msg)
{
    cout << "[" << __PRETTY_FUNCTION__ << "] " << msg << endl;
}

class CTest
{
private:
    map<string, callBackFunction> mapMethod;

    void testMethod(string msg)
    {
        cout << "[" << __PRETTY_FUNCTION__ << "] " << msg << endl;
    }
public:
    CTest()
    {
        addFreeFunction("AA", testFunction);
        addMemberFunction("BB", &CTest::testMethod);
    }
    void addMemberFunction(string funName, void(CTest::*methodName)(string))
    {
        using std::placeholders::_1;
        mapMethod[funName] = std::bind(methodName, this, _1);
    }
    void addFreeFunction(string funName, void(*methodName)(string))
    {
        mapMethod[funName] = methodName;
    }
    callBackFunction getMethod(string funName)
    {
        auto fun = mapMethod.find(funName);
        if(fun == mapMethod.end()) { return nullptr; }
        return fun->second;
    }
    void runFunction(string funName)
    {
        getMethod(funName)("test");
    }

};

int main()
{
    CTest test;
    test.runFunction("AA");
    test.runFunction("BB");

    return 0;
}

Notice that CTest must insert elements into the map in a different way depending on what type of function you are passing, since for member functions you must provide the object for which it is to be invoked, this in this example. This is achived by using std::bind.

Antonio Pérez
  • 6,702
  • 4
  • 36
  • 61
  • thanks. Your code worked. But now the callback method signature is hidden. I need to also pass callback methods from main. For ex: void tst2(string s) {cout << "New " << s;} .... and inside main(){ .... test.addFunction("BB", &tst2); ...} fails with compile error. Is there a better way to handle both this case. Thanks a lot. – programmer Dec 21 '15 at 13:06
  • So you need to use both member and non-member functions? – Antonio Pérez Dec 21 '15 at 13:08
  • yes. I need to use both member and non-member functions. – programmer Dec 21 '15 at 13:09
  • What we do in this case is to have everything overloaded for both member and non member. This is in really old code and might not be the best way but at the time it worked well. We moved over to [Qt](http://www.qt.io/) and use the signals and slots now instead. – CJCombrink Dec 21 '15 at 13:15
  • @The Badger, do you have an example to share? – programmer Dec 21 '15 at 13:28
  • @programmer I have updated my answer to show how we do it, but rather look at this answer for a better solution. I am also struggling with this exact issue for new code: http://stackoverflow.com/questions/34397819/using-stdfunction-and-stdbind-to-store-callback-and-handle-object-deletion – CJCombrink Dec 21 '15 at 14:27
  • @programmer Look at the answer to my question link above for a C++11 based approach. I had the code and question typed up a while back but your question made me revisit the problem and post the question. – CJCombrink Dec 22 '15 at 06:05