Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing pointer to member function to parent in c++

Tags:

c++

How can I make the following code work? I can't make the members static, Parent doesn't know about Child and I don't have access to boost. The reason I don't use virtual functions is that a Child class should be able to define 1-N handlers.

class Parent
{
public:
    void registerFileHandler(string ext, memFuncPtr);
};

class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg", &Child::loadJpg);
        registerFileHandler("png", &Child::loadPNG);
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
};

EDIT: There were many answers. The ones that works best for me use the keywords erasure, std::bind and std::function which of course rely on c++11. Here is a full compilable example:

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

using namespace std;

class Parent
{
public:
    void load(string filename)
    {
        // See if we can find a handler based on the extension.
        for(auto it = handlers.begin();it!=handlers.end();it++)
            if(filename.substr(filename.size()-it->first.size(), it->first.size())==it->first)
                it->second(filename);
    }
    template<typename Class>
    void registerFileHandler(Class* p, void (Class::*func)(string), string ext)
    {
        using namespace std::placeholders; //for _1, _2, _3...
        handlers[ext] = std::bind(func, p, _1);
    }
private:
    map<string, std::function<void(string)> > handlers;
};

class Child : public Parent
{
public:
    Child()
    {
        registerFileHandler(this, &Child::loadJpg, "jpg");
        registerFileHandler(this, &Child::loadPNG, "png");
    }
    void loadJpg(string filename)
    {
        cout << "loading the jpeg "<< filename << endl;
    }
    void loadPNG(string filename)
    {
        cout << "loading the png "<< filename << endl;
    }
};


int main(int argc, char* argv[])
{
    Child child;
    child.load("blah.jpg");
    child.load("blah.png");
    return 0;
}
like image 941
bgp2000 Avatar asked Dec 26 '22 04:12

bgp2000


2 Answers

How about std::function and std::bind:

class Parent
{
public:

    void registerFileHandler(string ext, const std::function<void(string)> &f)
    {
    }
};

class Child : public Parent
{
public:
    Child()
    {
        using namespace std::placeholders; //for _1, _2, _3...
        registerFileHandler("jpg", std::bind(&Child::loadJpg, this, _1));
        registerFileHandler("png", std::bind(&Child::loadPNG, this, _1));
    }

    ... 
like image 194
masoud Avatar answered Jan 04 '23 22:01

masoud


You need some form of type erasure. Assuming you can't use any of the sophisticated ones that already exist (boost::function, std::function) then you can roll your own:

class MemFuncPtr {
    void *obj;
    void (*caller)(void*, string);
  public:
    MemFuncPtr(void *obj, void(*caller)(void*, string)) : obj(obj), caller(caller) {}
    void operator()(string filename) {
        caller(obj, filename);
    }
};

class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg", MemFuncPtr(this, &jpgcaller));
        registerFileHandler("png", MemFuncPtr(this, &pgncaller));
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
  private:
    static void jpgcaller(void *obj, string filename) {
        static_cast<Child*>(obj)->loadJpg(filename);
    }
    static void pngcaller(void *obj, string filename) {
        static_cast<Child*>(obj)->loadPng(filename);
    }
};

I think you could get rid of those static functions using a function template with a pointer-to-member template parameter. But I'd probably make a mess of that code if I wrote it without testing it...

like image 28
Steve Jessop Avatar answered Jan 04 '23 22:01

Steve Jessop