bbill
bbill

Reputation: 2304

C++ Polymorphic Interfaces

I'm trying to create a way to handle many different versions of a protocol, similar to the question How to handle different protocol versions transparently in c++?. I agree that a chain of inheritance would solve the problem well.

This is how I would do it in Java: create an IParser interface, and have several ParserV0, ParserV1, ... classes, inheriting from each other and implementing IParser.

I understand that creating that structure is possible in C++ due to some multiple inheritance and virtual tricks. Here's the catch: in Java, if I wanted a parser, I think, I would be able to say IParser parser = getCorrectVersion(...). I would get some version of ParserV0, etc., implementing IParser and call the methods I need.

What's the equivalent to that last step in C++? There doesn't seem to be a way for me to ask for any class implementing another and then being able to call its methods.

EDIT:

Here's my attempt at doing what jtedit has suggested, since I have seen similar suggestions around StackOverflow:

class IParser {
public:
    virtual const string doSomething(params) = 0;
};

class ParserPreVersion : public IParser {
public:
    const string doSomething(params) {
        // do some thing
    }
};

class ParserFactory {
...
public:
    const IParser* generateParser() {
        return new ParserPreVersion();
    }
};

In another part of my code, I say

const IParser parser = *(ParserFactory().generateParser());

I've been getting compile-time errors with this code and variations of it, though, leading me to ask this question.

In member function 'const IParser* generateParser()':
error: cannot allocate an object of abstract type 'ParserPreVersion'
note:   because the following virtual functions are pure within 'ParserPreVersion':
note:      virtual const std::string IParser::doSomething(params)

also

error: cannot declare variable 'parser' to be of abstract type 'const IParser'
note:   since type 'const IParser' has pure virtual functions

I don't understand exactly why I have the first one, but the second is somewhat expected, and was the main concern in my question.

EDIT 2

And my attempt at what Scis has suggested (code declaring classes and functions is the same)

unique_ptr<IParser> getParser() {
    return unique_ptr<IParser>(new ParserV0());
}

auto parser(getParser());

This time, I get a vtable lookup error, since it seems be looking for the function definition in IParser.

FINAL EDIT:

I realized my code was a little messy and I was missing some modifiers for parameters, so that the virtual one and the overriding one didn't match up. The errors make a lot of sense. Thank you for your help!

Upvotes: 4

Views: 1987

Answers (3)

UpAndAdam
UpAndAdam

Reputation: 5467

You need an Abstract Factory, or a parametrized Factory Method; which is sorta what jtedit is describing. Usually in doing this you make the constructor themselves protected and only expose a factory method.. Check out the Gang of Four Creational Patterns for more information

http://en.wikipedia.org/wiki/Design_Patterns

Upvotes: 1

Scis
Scis

Reputation: 2984

You could use an abstract base class and create the required function as virtual as noted before the only thing I'd suggest is using unique_ptr and not raw pointers this way you'll not have to delete the memory yourself when you are finished using it (similarly to what you'd have in Java):

Here a sample:

unique_ptr<IParse> getParser(int x){
    switch (x){
        case 1:
            return unique_ptr<IParse>(new P1());
            break;
        case 2:
            return unique_ptr<IParse>(new P2());
            break;
    }
    return nullptr;
}

int main() {
    auto p1(getParser(1));
    p1->foo();

    auto p2(getParser(2));
    p2->foo();
    return 0;
}

where foo is declared as : virtual void foo() = 0;. See full example here.

Upvotes: 3

jtedit
jtedit

Reputation: 1470

Your getCorrectVersion() function needs to return a pointer to the interface, ie, the IParser

eg:

IParser* getCorrectVersion(int version){

    switch(version){
    case 0: 
         return new ParserV0();
    case 1:
         return new ParserV1();
    }
}

To be able to call the methods, the methods you want must be virtual in IParser

eg:

class IParser{
public:
    virtual int getInt() = 0;
};

Upvotes: 3

Related Questions