This is a C++(11) question.
I have a object Obj myObj encapsulating an object f of type MyType.
Depending on runtime context, the object fshould behave differently.
One natural way of implementing this would be for the class Obj to encapsulate a pointer to an abstract base class MyType, which would, depending on the context point to different (public) child of MyType, such as MyType1, MyType2, etc.
However, I'm not very keen on Obj "suffering" the consequences of MyType being polymorphic, i.e. having to deal with a pointer. In particular, if I make it a std::unique_ptr<MyType>, it implies that Obj can either not be copied or that one needs to give it a proper copy constructor that deals with copying MyType ressources.
In my opinion, MyType being polymorphic shouldn't be Obj's problem.
I came with the following classes. Essentially the idea is to hide the pointer within MyTypeprivate attributes. In addition my second question concerns the fact that concrete implementations of MyTypeImpl may share some code shouldn't be repeated. I've put that in a class from which concrete implementations privately inherit.
I'm curious what more expert developers than me would think about it. Is it too heavy "just to hide the pointer"? Is there a better way to do it?
#include <iostream>
#include <memory>
// a "standard" implementation of MyType
class MyTypeImpl
{
public:
virtual double operator()(double a) = 0;
virtual int implType() const = 0;
virtual void complexStuff() const = 0;
};
// some internal stuff common to all implementations
class MyTypeImplInternals
{
protected:
MyTypeImplInternals(int value):factor_{value}{}
int factor_;
void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};
// one specific implementation
class MyTypeImpl1: public MyTypeImpl, private MyTypeImplInternals
{
public:
MyTypeImpl1(int factor):MyTypeImplInternals{factor}{};
virtual double operator()(double a) override {return factor_*a;}
virtual int implType() const override {return 1;}
virtual void complexStuff() const override { longCommonFunction(); }
};
// a second implementation
class MyTypeImpl2: public MyTypeImpl, private MyTypeImplInternals
{
public:
MyTypeImpl2(int factor):MyTypeImplInternals{factor}{};
virtual double operator()(double a) override {return factor_*a;}
virtual int implType() const override {return 2;}
virtual void complexStuff() const override { longCommonFunction(); }
};
class MyTypeImplFactory
{
public:
static std::unique_ptr<MyTypeImpl>createMyTypeImpl(int implementationType)
{
switch(implementationType)
{
case 1:
return std::unique_ptr<MyTypeImpl> (new MyTypeImpl1(12));
case 2:
return std::unique_ptr<MyTypeImpl> (new MyTypeImpl2(22));
default:
throw std::runtime_error("implementation does not exist...\n");
return nullptr;
}
}
};
// my type
class MyType
{
public:
MyType(int implementationType)
{
implPtr_ = MyTypeImplFactory::createMyTypeImpl(implementationType);
}
MyType(MyType const& source)
: implPtr_{ MyTypeImplFactory::createMyTypeImpl(source.implType()) }
{
}
double operator()(double a){return (*implPtr_)(a);}
int implType() const {return implPtr_->implType();}
void complexStuff() const {implPtr_->complexStuff();}
private:
std::unique_ptr<MyTypeImpl> implPtr_;
};
class Obj
{
private:
MyType f;
public:
Obj(int dim):f{dim}{}
Obj(Obj&& sourceToMove) = default;
Obj(Obj const& source) = default;
void doStuff() {std::cout << "I'm doing stuff() " << f(2) << std::endl; f.complexStuff();}
};
int main()
{
Obj myObj{1}, myObj2{2};
myObj.doStuff();
myObj2.doStuff();
Obj myObj3{std::move(myObj2)}; // myObj2 now dead
Obj myObj4{myObj};
myObj3.doStuff();
myObj4.doStuff();
}
link to online compiler : http://cpp.sh/8rhyy
Here the implementations are very dumb ones to serve as an example. An application for this design could be for instance a Solver (Obj) that solves some kind of physics Equation (MyType) which exact definition depends on the dimensionality of the problem, equation in 1D space is not the same as in 2D or in 3D. Solver's code would be completely independent on Equation's dimensionality and also wouldn't have to deal with a pointer. Equation would hide its 1D, 2D, or 3D implementation from outside's world.
(was originally a post on code review that was put on Hold because to abstract)
This proposed class design appears to have an obvious problem. The polymorphic type is referenced by a std::unique_ptr:
std::unique_ptr<MyTypeImpl> implPtr_;
Obj's default copy constructor, and assignment operator will end up transferring the held pointer to the new object, leaving the std::unique_ptr in the original object staring at a nullptr. Not good.
At the bare minimum this should be either a std::shared_ptr, or Obj's copy constructor and assignment operator will need to instantiate a new implPtr_. Note that with the easy std::shared_ptr fix the result of the copy constructor and an assignment operator is having multiple instances of Obj referencing the same instance of MyTypeImpl, which may or may not be an issue.
A much simpler class design is simply have MyTypeImpl1 and MyTypeImpl2 be subclasses of Obj, implementing the required polymorphic behavior.
I just refactored your codes.
#include <iostream>
#include <memory>
// !abstraction
class MyType
{
public:
virtual double operator()(double a) = 0;
virtual int implType() const = 0;
virtual void complexStuff() const = 0;
};
// !!MyTypeImplInternals could be a super class of MyTypeImpl* if it has properties(such as factor_) or just some static functions.
class MyTypeImplInternals
{
public:
MyTypeImplInternals(int value):factor_{value}{}
int factor_;
void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};
// one specific implementation
class MyTypeImpl1: public MyType
{
MyTypeImplInternals internal_;
public:
MyTypeImpl1(int factor):internal_{factor}{};
virtual double operator()(double a) override {return internal_.factor_*a;}
virtual int implType() const override {return 1;}
virtual void complexStuff() const override { internal_.longCommonFunction(); }
};
// a second implementation
class MyTypeImpl2: public MyType
{
MyTypeImplInternals internal_;
public:
MyTypeImpl2(int factor):internal_{factor}{};
virtual double operator()(double a) override {return internal_.factor_*a;}
virtual int implType() const override {return 2;}
virtual void complexStuff() const override { internal_.longCommonFunction(); }
};
std::unique_ptr<MyType> createMyType(int implementationType)
{
switch(implementationType)
{
case 1:
return std::unique_ptr<MyType> (new MyTypeImpl1(12));
case 2:
return std::unique_ptr<MyType> (new MyTypeImpl2(22));
default:
throw std::runtime_error("implementation does not exist...\n");
return nullptr;
}
}
class Obj
{
private:
std::unique_ptr<MyType> f_;
public:
Obj(int dim):f_(createMyType(dim)){}
Obj(Obj&& sourceToMove) : f_(std::move(sourceToMove.f_)) {}
Obj(Obj const& source) : f_(createMyType(source.f_->implType())) {}
void doStuff() {std::cout << "I'm doing stuff() " << (*f_)(2) << std::endl; f_->complexStuff();}
};
int main()
{
Obj myObj{1}, myObj2{2};
myObj.doStuff();
myObj2.doStuff();
Obj myObj3{std::move(myObj2)}; // myObj2 now dead
Obj myObj4{myObj}; //!!Bad idea to share an implementation to more Objs.
myObj3.doStuff();
myObj4.doStuff();
}
Related
I have a base product class with a few private members and a public getter that derived classes inherit. I would like to disqualify instantiation, since the class is intended for use with an abstract factory. I thought protected con/destructors might work, however, this breaks my smart pointers. Friending seems like a useful disaster. Is there a well-known solution to this, or should I resign myself to the fact that any client who has the factory injected must also know enough to instantiate the base product?
class Product
{
private:
char type_name;
char size_name;
public:
Product(char, char);
virtual ~Product() {}
void Print();
};
Use a token key.
private:
Product(char, char);
struct key_t{explicit key_t(int){}};
static key_t key(){return key_t(0);}
public:
Product(key_t, char a, char b):Product(a,b){}
static std::shared_ptr<Product> make_shared(char a, char b){ return std::make_shared<Product>(key(),a,b); }
anyone with a Product::key_t can construct a Product without being a friend. And without the key, you cannot.
This lets Product pass creation-rights as a value.
Smart pointers with configurable destroy code can use similar techniques. But I'd just make the destructor public.
Your static member function, or friend function, which is the factory should have no problem with calling protected constructors and returning a smart pointer. Generally plan to return a std::unique_ptr<BaseClass> which can be converted into a std::shared_ptr if the caller wants that instead.
Make the virtual destructor public.
Update: Don't bother making the factory a friend. You only need to prevent the construction of the base and intermediate classes. Make them effectively hidden and private by hiding the implementation classes in their own source file. Or an anonymous namespace I suppose.
Here have some code of how I would do it:
#include <iostream>
#include <memory>
#include <string>
// ITest is the only class any other code file should ever see.
class ITest {
protected:
ITest() = default;
public:
virtual ~ITest() = 0;
virtual int getX() const = 0;
virtual int getY() const = 0;
};
// Destructors must always have an implementation even if they are pure virtual.
ITest::~ITest() {}
std::ostream &operator<<(std::ostream &os, const ITest &x) {
return os << '[' << x.getX() << ',' << x.getY() << ']';
}
// Declaration of constructTest factory function.
// Its definition should be hidden in a cpp file.
std::unique_ptr<ITest> constructTest(int x);
// The main function does not need to know anything except the ITest interface
// class and the constructTest function declaration.
int main(int argc, char *argv[]) {
int val = 0;
if (argc > 1)
val = std::stoi(argv[1]);
auto p = constructTest(val);
std::cout << *p << std::endl;
}
// These classes should be defined in a private header file or in a cpp file.
// Should not be visible to any other code. It has no business knowing.
// Hiding all of this implementation is sort of the point of abstract interface
// classes and factory function declarations.
class TestBase : public ITest {
private:
int x = 0;
int y = 0;
protected:
TestBase(int x = 0, int y = 0) : x(x), y(y){};
public:
int getX() const override { return x; }
int getY() const override { return y; }
};
class TestA final : public TestBase {
public:
TestA() = default;
};
class TestB final : public TestBase {
public:
TestB(int x, int y) : TestBase(x, y) {}
int getX() const override { return -TestBase::getX(); }
};
std::unique_ptr<ITest> constructTest(int x) {
// make_unique is c++14.
// For C++11 use std::unique_ptr<ITest>(new TestB(x, x)
if (x) {
return std::make_unique<TestB>(x, x);
// return std::unique_ptr<ITest>(new TestB(x, x));
}
return std::make_unique<TestA>();
}
The answer was to make the destructor a pure virtual AND to implement it with an empty body. That empty implementation is where I got tripped up. Print() doesn't need to be static.
Product.hpp
#include <memory>
class Product {
public:
virtual ~Product() = 0;
void Print();
protected:
char type_name{};
char size_name{};
private:
};
Product.cpp
#include "Product.hpp"
Product::~Product() {}
void Product::Print() {
//Print p
}
I have tried to understand what's going on in my situation from other questions in this site, but I haven't really found a good answer. I tried most of the suggestions I found but still get the same error.
I am trying to implement a factory based on a singleton and the CRTP. So I have a Singleton class, define in Singleton.h:
template<class T>
class Singleton
{
public:
static T &instance()
{
static T one;
return one;
}
Singleton(const Singleton &) = delete;
Singleton(Singleton &&) = delete;
Singleton &operator=(const Singleton &) = delete;
protected:
Singleton() = default;
};
I also have a Factory class, defined and implemented in Factory.h. The factory creates objects of a hierarchy whose base class is, for the purposes of this question, Object. These objects all have a constructor accepting a double.
class Factory : public Singleton<Factory>
{
friend class Singleton<Factory>; // to access constructor
public:
using createFunction = Object *(*)(double);
void registerObject(const std::string &, createFunction);
Object *createObject(const std::string &, double) const;
private:
Factory() = default;
std::map<std::string, createFunction> theCreatorFunctions;
};
void Factory::registerObject(
const std::string &ObjectId,
createFunction creatorFunction)
{
theCreatorFunctions.insert(
std::pair<std::string, createFunction>(
ObjectId, creatorFunction));
}
Object *Factory::createObject(
const std::string &ObjectId, double a) const
{
auto it = theCreatorFunctions.find(ObjectId);
if (it == theCreatorFunctions.end())
{
std::cout << ObjectId << " is an unknown object."
<< std::endl;
return nullptr;
}
return (it->second)(a);
}
Finally, I have a "helper" class that registers new types of Objects into the factory. Each time a new inherited object is created, say ObjectDerived, I add (in the .cpp file where ObjectDerived is implemented):
FactoryHelper<ObjectDerived> registerObjectDerived("ObjectDerived");
This creates an object of type FactoryHelper<ObjectDerived>, whose constructor handles the registration in the factory. FactoryHelper is defined (and implemented) in FactoryHelper.h:
template<class T>
class FactoryHelper
{
public:
FactoryHelper(const std::string &);
static Object *create(double);
};
template<class T>
FactoryHelper<T>::FactoryHelper(const std::string &ObjectId)
{
Factory &theFactory = Factory::instance(); // the one and only!
// if it doesn't exist at this point, it is created.
theFactory.registerObject(ObjectId, FactoryHelper<T>::create);
}
template<class T>
Object *FactoryHelper<T>::create(double a)
{
return new T(a);
}
So the problem that I have is that I get a bunch of undefined references to Factory::instance(), basically one for each type of object in the hierarchy.
If I put all in the same main.cpp file it works, but this is not a solution I'd like.
Since there is no compilation error when all your code is in one file, and you don't use any extern global objects that could cause issues with multiple files, I suspect that you have a problem in your compilation/linking script.
For the record, I can confirm that you have no intrinsic problem in the code. Adding a hierarchy
class Object
{
public:
Object(double _value) : value(_value) {}
virtual double getVal() { return value; }
private:
double value;
};
class SpecialObject : public Object
{
public:
SpecialObject(double _value) : Object(_value) {}
virtual double getVal() { double val = Object::getVal(); return val*val; }
};
the simple main routine
int main(int argc, char *argv[]) {
FactoryHelper<Object> baseMaker("Object");
FactoryHelper<SpecialObject> derivedMaker("SpecialObject");
Factory& factory = Factory::instance();
Object* a1 = factory.createObject("Object",4);
std::cout << a1->getVal() << std::endl;
Object* b1 = factory.createObject("SpecialObject",4);
std::cout << b1->getVal() << std::endl;
Object* c1 = factory.createObject("NonexistentObject",4);
return 0;
}
has the expected output:
4
16
NonexistentObject is an unknown object.
By the way, a matter of opinion: Your FactoryHelper<T> class does not achieve much, essentially acting as a shortcut for registering an object with the default allocator/constructor. At some point, making new classes stops actually saving much code. If you can use C++11, it's not much more difficult to write
factory.registerObject("SpecialObject", [] (double a) -> Object* { return new SpecialObject(a); });
If you wanted, you could add shortcut method to Factory itself:
// definition
template <class T>
void registerObject(const std::string &);
// implementation
template<class T>
void Factory::registerObject(const std::string &ObjectId)
{
registerObject(ObjectId, [] (double a) -> Object* { return new T(a); });
};
With this, the FactoryHelper class can be eliminated, and the equivalent main routine to before is
using namespace std;
int main(int argc, char *argv[]) {
Factory& factory = Factory::instance();
factory.registerObject<Object>("Object");
factory.registerObject<SpecialObject>("SpecialObject");
Object* a1 = factory.createObject("Object",4);
std::cout << a1->getVal() << std::endl;
Object* b1 = factory.createObject("SpecialObject",4);
std::cout << b1->getVal() << std::endl;
Object* c1 = factory.createObject("NonexistentObject",4);
return 0;
}
Again, if you are able to use C++11, you can always make createObject wrap the raw Object* pointer in a smart pointer (as you may well know, and maybe you have good reasons already for not doing this).
A case where 'problem' should not be a problem in the title.
I want to implement a solver (class Solver) for a collection of problems (all children of class Problem), which more or less share the same set of methods. My current design is like this:
In solver.h:
template<class P>
class Solver{
public:
P* p;
Solver(P* problem) : p(problem) {}
void justDoIt(){
p->step1();
p->step2();
p->step3();
p->step4();
p->step5();
}
}
In main.cpp:
#include "solver.h"
class A {
public:
void step1() {}
void step2() {}
void step3() {}
void step4() {}
void step5() {}
};
class B: public A {
public:
void step2() {}
void step4() {}
};
class C: public A {
public:
void step3() {}
void step4() {}
void step5() {}
};
int main(){
B b;
C c;
Solver<B> sb(&b);
Solver<C> sc(&c);
sb.justDoIt();
sc.justDoIt();
return 0;
}
If I want to extend Solver for a new problem type, say C, and it
does nothing in step1();
does step2.5() between step2() and step3()
Now calling C c; Solver<C> sc(&c); c.justDoIt(), I need to modify A, B and Solver::justDoIt() first.
Is there a scalable to design the interface that adding new problem types (all childern of A) for Solver?
PS: The current codebase I am about to modify has 47 types of problems all using the same Solver class. Minimal change is preferred.
How can I do it better?
At least to me this design seems like a (pardon the technical jargon) mess.
Right now, Solver has intimate knowledge of the internals of Problem. Further, it appears there's no way for Solver to do its job without intimate knowledge of the internals of Problem either.
At least in my estimation, what you've called Solver::justDoIt() should really be Problem::operator(). If many of the Problems use step1() through step5() as you've shown in Solver, you can provide that implementation by default in Problem itself, then those that need to override that will provide their own implementations:
class Problem {
protected:
virtual void step1() {}
// ...
virtual void step5() {}
public:
virtual void operator()() {
step1();
step2();
step3();
step4();
step5();
}
};
class B : public Problem {
protected:
void step2() {}
void step4() {}
};
class C : public Problem {
protected:
virtual void step3() {}
virtual void step4() {}
virtual void step5() {}
};
Then the main looks something like this:
int main() {
B b;
C c;
b();
c();
}
Or, if you prefer shorter code:
int main() {
B()();
C()();
}
This creates a temporary object of each type, then invokes the operator() on that temporary object. I'm not particularly fond of it, but some people think it's great.
Virtual Functions:
The first option that should come into mind is to use virtual functions:
Redefine your Problem-class to contain a pure virtual function (that means: every child needs to reimplement it):
class Problem{
public:
virtual void allSteps()=0;
};
Redefine your Solver to call this special function:
class Solver{
public:
Problem* p;
Solver(Problem* prob):p(prob){}
void solve(){
p->allSteps();
}
};
And add an implementation in every child-class:
class MyProblem: public Problem{
public:
void step1(){
std::cout << "step1\n";
}
void step2(){
std::cout << "step1\n";
}
void stepx(int x){
std::cout << "step"<<x<<"\n";
}
void allSteps(){
step1();
step2();
stepx(3);
stepx(4);
}
};
And use your main-function as you did before:
int main() {
MyProblem myP;
Solver s(&myP);
s.solve();
return 0;
}
Try it here: http://ideone.com/NOZlI6
Function-Pointers/Objects
This is a slightly more complex solution, but depending on your needs (e.g. executing only a single step and then do something else) it might better fit your needs.
Whenever you see something like "foo1","foo2","foo3",... you should think of an array or a vector. And the same can be applied to your Problem:
First of all, redefine your "Problem" class to take an arbitrary amount of function pointers - or using c++, function objects:
class Problem{
public:
std::vector<std::function<void(void)>> functions;
};
Then all your Solver needs to do is to iterate over the function objects inside your Problems class:
class Solver{
public:
Problem* p;
Solver(Problem* prob):p(prob){}
void solve(){
for(auto func : p->functions)
func();
}
};
In order to register your classes functions properly, you need to remember that member-functions have an additional "hidden" parameter "this" that is a pointer to the class itself. But we can use std::bind to make a void(void) function out of any function we have. An alternative would be to use static functions, but since this should be easy to figure out, i will use the more complex way here:
class MyProblem: public Problem{
public:
void step1(){
std::cout << "step1\n";
}
void step2(){
std::cout << "step1\n";
}
void stepx(int x){
std::cout << "step"<<x<<"\n";
}
MyProblem(){
functions.push_back(std::bind(&MyProblem::step1,this));
functions.push_back(std::bind(&MyProblem::step2,this));
functions.push_back(std::bind(&MyProblem::stepx,this,3));
functions.push_back(std::bind(&MyProblem::stepx,this,4));
}
};
Your main-function will then be unaffected:
int main() {
MyProblem myP;
Solver s(&myP);
s.solve();
return 0;
}
Try it here: http://ideone.com/BmIYVa
Consider the case of having two pure virtual classes, say X and Y. Y specifies a pure virtual method that returns a smart pointer to an instance of X (e.g. virtual unique_ptr<X> getX() const = 0). This is done so that subclasses of Y can return whatever implementation of X they desire.
However, this means that a user has to be aware that upon calling getX(), they should expect to work with an instance of unique_ptr<X> (not ideal). This is readily fixed by wrapping unique_ptr<X> in a class as in the following example:
#include <iostream> // cout, endl
#include <memory> // unique_ptr
using namespace std;
////////////////////////////////////////////////////////////////////////////////
struct X {
virtual void exampleMethod() = 0;
};
struct XCloneable : public X {
typedef unique_ptr<XCloneable> P;
virtual P clone() const = 0;
};
class XWrapper : public X {
XCloneable::P p;
public:
XWrapper(XCloneable::P p) noexcept : p(move(p)) {}
XWrapper(const XWrapper &that) noexcept : p(that.p->clone()) {}
XWrapper &operator=(const XWrapper &that) noexcept {
p = that.p->clone();
return *this;
}
XWrapper(XWrapper &&that) noexcept : p(move(that.p)) {
}
XWrapper &operator=(XWrapper &&that) noexcept {
p = move(that.p);
return *this;
}
virtual void exampleMethod() { p->exampleMethod(); }
};
////////////////////////////////////////////////////////////////////////////////
struct XX : public XCloneable {
virtual void exampleMethod() { cout << "XX" << endl; }
virtual XCloneable::P clone() const { return XCloneable::P(new XX); }
};
////////////////////////////////////////////////////////////////////////////////
struct Y {
virtual XWrapper getX() = 0;
};
struct YY {
virtual XWrapper getX() { return XWrapper(XCloneable::P(new XX)); }
};
////////////////////////////////////////////////////////////////////////////////
int main() {
YY yy;
auto x = yy.getX();
x.exampleMethod();
return 0;
}
However, this is quite verbose, and has to be written for each pure virtual class similar to X. I imagine it would not be too difficult to automatically generate wrappers such as the above systematically, although I would prefer to not run my code through anything other than the usual C preprocessor (although more exotic preprocessing solutions are welcome/interesting).
Is there a way to handle this scenario systematically?
The pattern you're talking about is an Abstract Factory. I'm really not sure why most of the code in your question exists though... Here is an example of an abstract factory implementation which should do what you need:
#include <iostream>
#include <memory>
class X
{
public:
virtual void exampleMethod() = 0;
};
class MyX : public X
{
public:
void exampleMethod() override
{
std::cout << "Calling MyX::exampleMethod()";
}
};
class XFactory
{
public:
virtual std::unique_ptr<X> createX() = 0;
static XFactory* getInstance()
{
return m_instance.get();
}
static void setInstance(std::unique_ptr<XFactory> instance)
{
m_instance = move(instance);
}
private:
static std::unique_ptr<XFactory> m_instance;
};
std::unique_ptr<XFactory> XFactory::m_instance = std::unique_ptr<XFactory>();
class MyXFactory : public XFactory
{
public:
std::unique_ptr<X> createX() override
{
return std::unique_ptr<X>(new MyX);
}
};
int main()
{
// Call setInstance with different XFactory implementations to get back
// different implementations of X.
std::unique_ptr<XFactory> xFactory(new MyXFactory);
XFactory::setInstance(move(xFactory));
std::unique_ptr<X> x = XFactory::getInstance()->createX();
x->exampleMethod();
return 0;
}
This example outputs:
Calling MyX::exampleMethod()
I don't see that you need a wrapper at all although there is no reason you couldn't return one from MyXFactory::createX() as long as it extends X.
EDIT:
I just re-read your question, why is it not ideal for the caller to know they are dealing with a unique_ptr? I would think that it is the most ideal. By giving them a unique_ptr you are explicitly saying to them: you own this now.
Let's say I have the following code:
struct Z;
struct A
{
virtual void Do (Z & z) const;
};
struct B : public A {};
struct Z
{
void use (A const & a) {}
void use (B const & b) {}
};
void A::Do(Z& z) const{
z.use(*this);
}
Right now, when I call B.do, the type of this is A, which make sense, because the implementation of do is defined in A.
Is there any way to have calls to B.do use use (B const &) without having to copy-paste the same code for do from A into B? In my actual code I have about 15 (and growing) classes derived from some base class and it seems a waste having to copy-paste the identical code for do everytime.
[Edit] Clarification: all Do does is call use, nothing else. Do and use are the accept & visit functions from the Visitor pattern.
Since you now clarified that what you want is the visitor pattern, well, sorry, but that's just how it is. This answer shows how the visitor pattern with double dispatch works.
I thought of a nice way using CRTP, but this may or may not work for you, depending on the circumstances.
(Note: I used the code from the linked answer, so the names don't match, but I hope you get the idea.)
// your Z
class Visitor;
// superclass needed for generic handling
struct Superbase{
virtual void Accept(Visitor& v) = 0;
};
// your A
template<class Der>
class Base : public Superbase{
public:
void Accept(Visitor& v){
v.Visit(static_cast<Der&>(*this));
}
};
// your B
class Derived1 : public Base<Derived1> {
};
// new C
class Derived2 : public Base<Derived1> {
};
class Visitor {
public:
virtual void Visit(Superbase& sup){
// generic handling of any Superbase-derived type
}
virtual void Visit(Derived1& d1){
// handle Derived1
}
virtual void Visit(Derived2& d2){
// handle Derived1
}
};
int main(){
Visitor v;
Derived1 d1;
d1.Accept(v);
}
The only problem: Now you're missing the chance to have a generic handle to any type of A, since functions can't be both virtual and templates. :|
Scrape that, found a solution using a Superbase base class. :) This even allows you to have a container of Superbases and take full advantage of polymorphism. :)
I think this code does what you want:
#include <iostream>
struct A;
struct B;
struct Z
{
void use (A const & a);
void use (B const & b);
};
template<typename DERIVED>
struct XX
{
void Do(Z& z){
Do(z,THIS());
}
private:
const DERIVED& THIS() const { return static_cast<const DERIVED&>(*this); }
void Do(Z& z, const DERIVED& t){
z.use(t);
}
};
struct A : public XX<A> {};
struct B : public XX<B> {};
void Z::use (A const & a) { std::cout << "use for A" << std::endl; }
void Z::use (B const & b) { std::cout << "use for B" << std::endl; }
int main(){
A a;
B b;
Z z;
a.Do(z);
b.Do(z);
return 0;
}
The only 'maintenance' or 'boiler-plate' part of the code is to derive from the template class templated on your own type.
You need to dispatch the call of use based on the type pointed to by this so you need to add another virtual function to A and B that simply invokes the correct use. I assume that do does other things than call use of course otherwise you would indeed have to re-implement do in each subclass. It would look like this
struct A
{
virtual void Do (Z & z) const
{
// do stuff
use(z);
// do more stuff
}
virtual void use(Z & z) const
{
z.use(*this);
}
};
struct B : public A
{
virtual void use(Z & z) const
{
z.use(*this);
}
};
struct Z
{
void use (A const & a) {}
void use (B const & b) {}
};
I think I have to disappoint you and say no. This is the trade off you have to make, in order for you to break out the interface from your classes into the visitor. The visitor must know which one is reporting to it, as long as you don't override the virtual Do() in the base class, the visitor will treat you as A.
Please someone prove me wrong! (I'd also see this solved to remove redundancy)