I have a lot of different child classes (15 ) that all have the same function centerAt
defined inside of them. Those child classes each inherits a different classes in an extern library: what is mutual for all of them is that they have the function setPos
that centerAt is dependent on.
My goal is to avoid code repetition and I therefore came up with a solution moving centerAt to a base class that all the child class could inherit along side with the base classes from the external library: multiple inheritance.
I came up with this code
class InternalBase {
public:
void centerAt(int x, int y) {
// Do some math
setPos(newx, newy)
}
virtual void setPos() = 0; // Should be replaced by the setPos in ExternalBase
};
// There are 15 different external base classes used, here only 2 are shown
class ExternalBase1 { // this is from an external library
public:
void setPos() { /* internal definition */}
};
class ExternalBase2 { // this is from an external library
public:
void setPos() { /* internal definition */}
};
class Child1 : public InternalBase, public ExternalBase1 {
public:
// A lot of different functions
};
class Child2 : public InternalBase, public ExternalBase2 {
public:
// A lot of different functions
};
int main() {
Child1 myobj;
myobj.centerAt(120, 394);
return 0;
}
Unfortunately, the object can't be constructed: the compiler complaining that is an abstract class - probably because setPos
weren't inherited from ExternalBase1
. How can I fix this, so Child1
inherits setPos
from ExternalBase1
and InternalBase
thereby can use the setPos
defined in ExternalBase1
.
This issue is identical to Override pure virtual method with parallel base class method?, but the big knot is, that because I am dealing with an external library, it is not possible to use the given solution (making ExternalBase1
inherit InternalBase
first: class ExternalBase1: public virtual InternalBase
).
I would like to avoid the use of templates.
CodePudding user response:
Using templates is the simplest solution. Templates are one of the distinct features of C and there's no reason not to use them.
template<typename T>
class ExternalBaseMixin : public InternalBase, public T {
public:
using T::T;
void setPos(int x, int y) override
{
T::setPos(x, y);
}
};
That's it. Then, it's simply a matter of:
class Child1 : public ExternalBaseMixin<ExternalBase1> {
// ...
class Child2 : public ExternalBaseMixin<ExternalBase2> {
// ...
etc...
There is no comparable built-in, non-template C feature that accomplishes the same thing. Any non-template solution will end up doing the exact same thing that this does, but with much more typing, more code, and with a more error-prone result. This is fairly bulletproof, as long as a single requirement gets met: always inherit from ExternalBaseMixin
. As long as that happens everything gets overriden and combined together, in the proper order. Plus, this also guarantees inheritance order: that InternalBase
gets constructed before the external class, if that matters.
CodePudding user response:
You can use CRTP and make InternalBase
a template type
.
This introduces very little changes to your code.
template <class Derived>
class InternalBase {
public:
void centerAt(int x, int y) {
// Do some math
Derived& derived = static_cast<Derived&>(*this);
derived.setPos(x, y);
}
};
class ExternalBase1 {
public:
void setPos(int x, int y) {}
};
class Child1 : public InternalBase<Child1>, public ExternalBase1 {
};
int main() {
Child1 myobj;
myobj.centerAt(120, 394);
return 0;
}
See it work here: https://godbolt.org/z/Tsej4Gcfd
CodePudding user response:
A quick fix for your problem is just explicitly defining on each child the setPos() function to use. (I adjusted some ill defined code that I guess comes from this being an example, but setPos and the virtual definitions provided did not match in signature):
#include <iostream>
class InternalBase {
public:
void centerAt(int x, int y) {
// Do some math
setPos(x, y);
}
virtual void setPos(int x, int y) = 0;
};
class ExternalBase1 {
public:
void setPos(int x, int y) { std::cout << "Inside ExternalBase1::setPos" << std::endl; }
};
class Child1 : public InternalBase, public ExternalBase1 {
public:
void setPos(int x, int y) { ExternalBase1::setPos(x, y); }
// A lot of different functions
};
int main() {
Child1 myobj;
myobj.centerAt(120, 394);
return 0;
}
Although I would suggest, if you have the time, to try to solve the problem structuring the code from a different approach (templates are ideal here, as already someone answered).