lcdavis13
lcdavis13

Reputation: 119

C++ Avoiding redefinition of code that needs to return a polymorphic type

I have a set of classes (SubClassA, SubClassB, etc) that inherit from SuperClass. These classes have a myriad of methods that are exactly identical between the subclasses, except that they return a reference to *this (a.k.a. SubClassX&). The return type can't be deprecated to SuperClass& either, because then the subclass-specific functions and members wouldn't be accessible.

I'm looking for a short, clean way to implement this so that I don't have to replicate the method code in each subclass.

The best thing I've been able to come up with is something like this (pseudo-code), but I'm not sure if this would even work:

//methods.cpp
SUBCLASS& general_method_1() {return *this;}
SUBCLASS& general_method_2() {return *this;}

And then:

#define SUBCLASS SubClassA
class SubClassA : public SuperClass
{
     #include "methods.cpp"

     SubClassA& specific_method_1() {return *this;}
}

#define SUBCLASS SubClassB
class SubClassB : public SuperClass
{
     #include "methods.cpp"

     SubClassB& specific_method_2() {return *this;}
}

Not to mention this seems kind of hackish and difficult to interpret. Any ideas?

EDIT: I should have mentioned that I will need to be able to polymorphically access the subclasses through pointers to SuperClass. For example, the following needs to be valid:

SuperClass* subclass[2];
subclass[0] = new SubClassA;
subclass[1] = new SubClassB;

Upvotes: 2

Views: 338

Answers (4)

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

CRTP.

template <class Derived> class Base
{
    Derived& derived() { return static_cast<Derived&>(*this); }
};

class Derived1: public Base<Derived1>
{
};

class Derived2: public Base<Derived2>
{
};

In order to access things polymorphically, one needs to split Base in two:

class Base
{
  public:
    virtual ~Base() {}
    // virtual Base& derived() = 0; -- note: this will NOT work!
};

template <class Derived> class BaseImpl : public Base
{
    Derived& derived() { return static_cast<Derived&>(*this); }
};

class Derived1: public BaseImpl<Derived1>
{
};

class Derived2: public BaseImpl<Derived2>
{
};

int main()
{
    Base* b[2] = { new Derived1, new Derived2 };
}

Upvotes: 1

Keith
Keith

Reputation: 6834

It's not clear from your question whether all you require is to make use of covariant return types and a template.

For example:

class SuperClass
{
public:
    virtual SuperClass& method() = 0;
};

class SpecificA : public SuperClass
{
public:
    int specificMethodA();
};

class SpecificB : public SuperClass
{
public:
    int specificMethodB();
};

template<class Specific>
class SubClass : public Specific
{
public:

    virtual SubClass& method() { return *this; }
};

int main()
{
    SubClass<SpecificA> scA;
    SubClass<SpecificB> scB;

    scA.method().specificMethodA();
    scB.method().specificMethodB();
}

Upvotes: 0

bolov
bolov

Reputation: 75727

The way I would do it:

class Dystopia {
  using This = Dystopia;
  This& beHappy() {
    // make me happy
    return *this;
  }
};

class Utopia : public Dystopia {
  using This = Utopia;
  This& beHappy() {
    return static_cast<This&>(Dystopia::beHappy());
  }
};

The main point here is that when you are in a derived class you can explicitly call the base method.

Note that the typedef is just a commodity (is not required).
As a sidenode: avoid macros in C++. Can’t emphasise this enough: Avoid macros!!. Armageddon will come in macros and good luck then debugging the End of the world.

Upvotes: 1

R Sahu
R Sahu

Reputation: 206627

I am going to suggest the following:

  1. Create a .h file that contains one or more macros to declare the common functions.
  2. Create a .h file that contains one or more macros to define the common functions.
  3. Add the include file created in (1) in the class header files and use the macro(s) to declare the functions.
  4. Add the include file created in (2) in the class .cc files and the use the macro(s) to define the functions.

Example:

DeclareFunctionsMacros.h

#define DECLARE_FUNCTIONS(THIS_CLASS) \
THIS_CLASS& general_method_1();       \
THIS_CLASS& general_method_2();

DefineFunctionsMacros.h

#define DEFINE_FUNCTIONS(THIS_CLASS)                         \
THIS_CLASS& THIS_CLASS::general_method_1() { return *this; } \
THIS_CLASS& THIS_CLASS::general_method_2() { return *this; }

SubClassA.h

#include "DeclareFunctionsMacros.h"

class SubClassA : public SuperClass
{
   DECLARE_FUNCTIONS(SubClassA);  
   SubClassA& specific_method_1() {return *this;}
}

SubClassA.cc

#include "DefineFunctionsMacros.h"

DEFINE_FUNCTIONS(SubClassA)  

This will allow you to alter definitions of the macros used in defining the functions without needing to recompile everything that #includes SubClassA.h.

Upvotes: 0

Related Questions