How to return a member function pointer from a class method?

111 Views Asked by At

I am trying to use something like a strategy pattern, where there are two different Parsers Type1Parser and Type2Parser using an interface IParser. There is another class ParsingText where we have 2 methods

  1. setParser: which will set the parser type.
  2. getMethod return a method in the Parser. For eg. format or parse.
#include <iostream>
#include<vector>
#include<string>
using namespace std;

class IParser 
{
public:
    virtual ~IParser() = default;
    virtual string format() = 0;
    virtual string parse() = 0;
};

class Type1Parser :public IParser 
{
public:
    string format() override 
    {
        cout << " Formatting 1";
        return string();
    }
    string parse() override
    {
        cout << " Parsering 1";
        return string();
    }
};

class Type2Parser :public IParser 
{
public:
    string format() override 
    {
        cout << " Formatting 2";
        return string();
    }
    string parse() override 
    {
        cout << " Parsering 2";
        return string();
    }
};

typedef string(IParser::* parseFunc)();
class ParsingText
{
    IParser* parser;

public:
    void setParser(int p) 
    {
        if (p == 1) parser = new Type1Parser();
        if (p == 2) parser = new Type2Parser();
    }

    parseFunc getMethod(char o) 
    {
        if (o == 'f') return parser->format;
        else return parser->parse;
    }
};

int main() 
{
    ParsingText* pt = new ParsingText();

    pt->setParser(1);
    parseFunc myMethod = pt->getMethod('f');
    *myMethod();
    return 0;
}

I am using a function pointer to return this class method. But I am not able to do so. I'm not sure what I'm doing wrong here?

1

There are 1 best solutions below

0
JeJo On

I'm using a function pointer to return this class method. but I'm not able to do so. I'm not sure what I'm doing wrong here?

In the getMethod you are trying to call the corresponding member function, rather than returning it. The correct syntax is as follows:

parseFunc getMethod(char o)
{
    if (o == 'f') return &IParser::format;  // ---> like this
    else          return &IParser::parse;   // ---> like this
}

This will resolve the compiler error. However, you have now next issue. In order to call the pointer to member function, you need a corresponding object instance as well, which is only privately available in ParsingText class. One solution is to provide a getter for parser instance, and invoke the pointer to member with that.

class ParsingText 
{
    // Use smart pointer rather than raw pointer(ex. std::unique_ptr)
    IParser* parser{ nullptr };
public:

    //  MEMORY LEAKS in setParser() !!!

    parseFunc getMethod(char o)
    {
        if (o == 'f') return &IParser::format;
        else          return &IParser::parse;
    }
    // Provided a getter for IParser
    IParser* getParser() { return parser; }
};

int main() 
{
    // Use smart pointer rather than raw pointer(ex. std::unique_ptr)
    auto pt{std::make_unique<ParsingText>()};

    pt->setParser(1);

    parseFunc myMethod = pt->getMethod('f');
    
    if (auto parser = pt->getParser())
    {
        (parser->*myMethod)(); // invoke with the Parser instance
        // or since C++17, from  <functional> header
        // std::invoke(myMethod, parser);
    }
    return 0;
}

See live demo in godbolt.org


That being said, this is not probably how you want to handle this case. The caller side could be much cleaner, if you had returned the wrapped version of the invoked member function with the correct IParser instance. The std::function comes in handy here.

class ParsingText 
{
    // Use smart pointer rather than raw pointer(ex. std::unique_ptr)
    IParser* parser{ nullptr };
public:
    //  MEMORY LEAKS in setParser()!!!

    std::function<std::string()> invokeMethod(char o)
    {
        if (o == 'f' && parser) return [this] {return parser->format(); };
        else if (parser)        return [this] {return parser->parse(); };
        else return {}; // or some error handling
    }
};

int main() 
{
    // Use smart pointer rather than raw pointer(ex. std::unique_ptr)
    auto pt{std::make_unique<ParsingText>()};
    pt->setParser(1);

    auto callable = pt->invokeMethod('f'); // invoke like this
    callable();
}

See live demo in godbolt.org