This question already has an answer here:
It is a simple, template-less Factory design pattern implementation as part of a hobby project. The goals:
- To implement it with the possibly most clean interface.
- With the restriction that templates can't be used.
- The library classes need to be able to be inherited virtually.
- Eliminating the by value returns in the cases of
A a = B.make()
calls. This is achieved by thatB.make()
returns an intermediate object (Factory::Mediator
), andA::A(Factory::Mediator)
calls a callback toB
's real creator method.
My problem is, that downcasting in Factory.cc:73
(in the line A* a = dynamic_cast<A*>(r);
) doesn't work, but it should.
My factory.h
:
#ifndef factory_h
#define factory_h
class IAmPolymorphicNow {
private:
virtual void iAmPolymorphicNow();
protected:
IAmPolymorphicNow();
};
namespace Factory {
class Factory : private IAmPolymorphicNow {
};
class Mediator;
class Result : private IAmPolymorphicNow {
public:
Result();
Result(Mediator fm);
};
typedef void (*MakeMethod)(Factory* factory, Result* result);
class Mediator {
private:
Factory* factory;
MakeMethod makeMethod;
public:
Mediator(Factory* factory, MakeMethod makeMethod);
void call(Result* result);
};
};
#endif
My factory.cc
(contains also the minimal test case):
#include <iostream>
#include "factory.h"
using namespace std;
void IAmPolymorphicNow::iAmPolymorphicNow() {
// nothing now
};
IAmPolymorphicNow::IAmPolymorphicNow() {
// nothing now
};
Factory::Result::Result() {
cout << "Factory::Result::Result()n";
};
Factory::Result::Result(Mediator fm) {
cout << "Factory::Result::Result(Mediator)n";
fm.call(this);
};
Factory::Mediator::Mediator(Factory* factory, MakeMethod makeMethod) {
cout << "Factory::Mediator::Mediator(Factory*, MakeMethod)n";
this->factory = factory;
this->makeMethod = makeMethod;
};
void Factory::Mediator::call(Result* result) {
cout << "Factory::Mediator::call(Result*)n";
(*makeMethod)(factory, result);
};
class A;
class B : public virtual Factory::Factory {
private:
int v;
public:
B(int v);
int getV() const;
static void makeCb(Factory* f, ::Factory::Result* a);
::Factory::Mediator make();
};
class A : public virtual Factory::Result {
friend class B;
private:
int v;
public:
A();
A(Factory::Mediator fm);
int getV() const;
void setV(int v);
};
B::B(int v) {
cout << "B::B()n";
this->v = v;
};
int B::getV() const {
cout << "B::getV()n";
return v;
};
void B::makeCb(Factory* f, ::Factory::Result* r) {
cout << "B::makeCb(Factory*, Factory::Result*)n";
B* b = dynamic_cast<B*>(f);
A* a = dynamic_cast<A*>(r);
a->setV(b->getV()+1);
};
Factory::Mediator B::make() {
cout << "Factory::Mediator B::make()n";
return ::Factory::Mediator(static_cast<Factory*>(this), &B::makeCb);
};
A::A() {
cout << "A::A()n";
v = 0;
};
A::A(Factory::Mediator fm) : Factory::Result(fm) {
cout << "A::A(Factory::Mediator)n";
};
int A::getV() const {
cout << "A::getV()n";
return v;
};
void A::setV(int v) {
cout << "A::setV(" << v << ")n";
this->v = v;
};
int main(int argc, char **argv) {
B b(42);
A a = b.make();
cout << "a.v = " << a.getV() << "n";
return 0;
}
How the problem can be reproduced:
$ g++ -o factory factory.cc -g -Wall -std=c++11
$ gdb ./factory
Reading symbols from ./factory...done.
(gdb) run
Starting program: ./factory
B::B()
Factory::Mediator B::make()
Factory::Mediator::Mediator(Factory*, MakeMethod)
Factory::Result::Result(Mediator)
Factory::Mediator::call(Result*)
B::makeCb(Factory*, Factory::Result*)
B::getV()
A::setV(43)
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400da6 in A::setV (this=0x0, v=43) at factory.cc:98
98 this->v = v;
(gdb) bt
#0 0x0000000000400da6 in A::setV (this=0x0, v=43) at factory.cc:98
#1 0x0000000000400b7a in B::makeCb (f=0x7fffffffe610, r=0x7fffffffe600) at factory.cc:74
#2 0x0000000000400a08 in Factory::Mediator::call (this=0x7fffffffe590, result=0x7fffffffe600) at factory.cc:31
#3 0x0000000000400990 in Factory::Result::Result (this=0x7fffffffe600, fm=...) at factory.cc:20
#4 0x0000000000400d0e in A::A (this=0x7fffffffe600, fm=..., __in_chrg=<optimized out>, __vtt_parm=<optimized out>) at factory.cc:87
#5 0x0000000000400ded in main (argc=1, argv=0x7fffffffe718) at factory.cc:103
(gdb) frame 1
#1 0x0000000000400b7a in B::makeCb (f=0x7fffffffe610, r=0x7fffffffe600) at factory.cc:74
74 a->setV(b->getV()+1);
(gdb) print a
$1 = (A *) 0x0
(gdb) print b
$2 = (B *) 0x7fffffffe610
(gdb) list
69
70 void B::makeCb(Factory* f, ::Factory::Result* r) {
71 cout << "B::makeCb(Factory*, Factory::Result*)n";
72 B* b = dynamic_cast<B*>(f);
73 A* a = dynamic_cast<A*>(r);
74 a->setV(b->getV()+1);
75 };
76
77 Factory::Mediator B::make() {
78 cout << "Factory::Mediator B::make()n";
Note: This code is already a minimal sanitized example, but for a suggestion how could it be made yet smaller, I would be glad to further shorten it.
Aucun commentaire:
Enregistrer un commentaire