1

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?

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • 2
    Is the problem that your computer catches on fire when you run it? If so, it's probably not the code posted here. On the other hand, if you explain exactly what "not able to do so" means (compiler error, run time crash, wrong output, etc.) then someone might be able to help you. – John Bayko Jun 24 '23 at 16:43
  • The complier throws a message that a pointer to a bound function may only be used to call the function – Johnson Jose Jun 24 '23 at 16:51
  • 1
    In `getMethod`, make it `return &IParser::format;` - that'll get you past the error message. However, in order to call via a pointer-to-member-function, you need an object (an instance of `IParser` or a derived class) to call it on. An instance of `parseFunc` only points to a method, but not an object to call it on. If you want to bundle both together, you'd need to return e.g. `std::function`, then `getMethod` could do `return [=]() { return parser->format(); };` – Igor Tandetnik Jun 24 '23 at 16:59
  • Why do you want to return a function rather than just invoke the function? – John Zwinck Jun 24 '23 at 17:50

1 Answers1

2

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

JeJo
  • 30,635
  • 6
  • 49
  • 88