dimanche 26 juin 2016

Mystical downcast failure [duplicate]

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:

  1. To implement it with the possibly most clean interface.
  2. With the restriction that templates can't be used.
  3. The library classes need to be able to be inherited virtually.
  4. Eliminating the by value returns in the cases of A a = B.make() calls. This is achieved by that B.make() returns an intermediate object (Factory::Mediator), and A::A(Factory::Mediator) calls a callback to B'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