Question

At the moment I'm dealing with a delightful legacy code class implementing polymorphism by switch-case:

class LegacyClass {
 public:
    enum InitType {TYPE_A, TYPE_B};
    void init(InitType type) {m_type=type;}

    int foo() {
        if (m_type==TYPE_A) 
        { 
            /* ...A-specific work... */
            return 1;
        }
        // else, TYPE_B:
        /* ...B-specific work... */
        return 2;
    }

    /** Lots more functions like this **/

  private:
    InitType m_type;
 };

I'd like to refactor this to proper polymorphism, e.g.:

class RefactoredClass {
 public:
    virtual ~RefactoredClass(){}
    virtual int foo()=0;
 };

 class Class_ImplA : public RefactoredClass {
 public:
    virtual ~Class_ImplA(){}
    int foo() {
        /* ...A-specific work... */
        return 1;
    }
 };

class Class_ImplB : public RefactoredClass {
 public:
    virtual ~Class_ImplB(){}
    int foo() {
        /* ...B-specific work... */
        return 2;
    }
 };

Unfortunately, I have one crucial problem: due to optimization and architectural considerations, within a primary use of LegacyClass, I cannot use dynamic allocation; the instance is a member of a different class by composition:

 class BigImportantClass{
    /* ... */
  private:
    LegacyClass m_legacy;
 }

(In this example, BigImportantClass may be dynamically allocated, but the allocation needs to be in one continuous virtual segment, and a single new() call; I can't make further calls to new() in the BigImportantClass ctor or in subsequent initialization methods.)

Is there a good way to initialize a concrete implementation, polymorphically, without using new()?


My own progress so far: What I can do is provide a char[] buffer as a member of BigImportantClass, and somehow initialize a concrete member of RefactoredClass in that memory. The buffer would be large enough to accommodate all implementations of RefactoredClass. However, I do not know how to do this safely. I know the placement-new syntax, but I'm new to dealing with alignment (hence, warned off by the C++-FAQ...), and aligning generically for all concrete implementations of the RefactoredClass interface sounds daunting. Is this the way to go? Or do I have any other options?

Was it helpful?

Solution

Here's some code... just doing the obvious things. I don't use C++11's new union features, which might actually be a more structured way to ensure appropriate alignment and size and clean up the code.

#include <iostream>

template <size_t A, size_t B>
struct max
{
    static const size_t value = A > B ? A : B;
};

class X
{
  public:
    X(char x) { construct(x); }

    X(const X& rhs)
        { rhs.interface().copy_construct_at_address(this); }

    ~X() { interface().~Interface(); }

    X& operator=(const X& rhs)
    {
        // warning - not exception safe
        interface().~Interface();
        rhs.interface().copy_construct_at_address(this);
        return *this;
    }

    struct Interface
    {
        virtual ~Interface() { }
        virtual void f(int) = 0;
        virtual void copy_construct_at_address(void*) const = 0;
    };

    Interface& interface()
        { return reinterpret_cast<Interface&>(data_); }

    const Interface& interface() const
        { return reinterpret_cast<const Interface&>(data_); }

    // for convenience use of virtual members...
    void f(int x) { interface().f(x); }

  private:
    void construct(char x)
    {
             if (x == 'A') new (data_) Impl_A();
        else if (x == 'B') new (data_) Impl_B();
    }

    struct Impl_A : Interface
    {
        Impl_A() : n_(10) { std::cout << "Impl_A(this " << this << ")\n"; }
        ~Impl_A() { std::cout << "~Impl_A(this " << this << ")\n"; }
        void f(int x)
            { std::cout << "Impl_A::f(x " << x << ") n_ " << n_;
              n_ += x / 3;
              std::cout << " -> " << n_ << '\n'; }
        void copy_construct_at_address(void* p) const { new (p) Impl_A(*this); }
        int n_;
    };

    struct Impl_B : Interface
    {
        Impl_B() : n_(20) { std::cout << "Impl_B(this " << this << ")\n"; }
        ~Impl_B() { std::cout << "~Impl_B(this " << this << ")\n"; }
        void f(int x)
            { std::cout << "Impl_B::f(x " << x << ") n_ " << n_;
              n_ += x / 3.0;
              std::cout << " -> " << n_ << '\n'; }
        void copy_construct_at_address(void* p) const { new (p) Impl_B(*this); }
        double n_;
    };

    union
    {
        double align_;
        char data_[max<sizeof Impl_A, sizeof Impl_B>::value];
    };
};

int main()
{
    {
        X a('A');
        a.f(5);

        X b('B');
        b.f(5);
        X x2(b);
        x2.f(6);
        x2 = a;
        x2.f(7);
    }
}

Output (with my comments):

Impl_A(this 0018FF24)
Impl_A::f(x 5) n_ 10 -> 11
Impl_B(this 0018FF04)
Impl_B::f(x 5) n_ 20 -> 21.6667
Impl_B::f(x 6) n_ 21.6667 -> 23.6667
~Impl_B(this 0018FF14)           // x2 = a morphs type
Impl_A::f(x 7) n_ 11 -> 13       // x2 value 11 copied per a's above
~Impl_A(this 0018FF14)
~Impl_B(this 0018FF04)
~Impl_A(this 0018FF24)

OTHER TIPS

I implemented this using C++11 unions. This code seems to work under g++ 4.8.2, but it requires the -std=gnu++11 or -std=c++11 flags.

#include <iostream>

class RefactoredClass {
  public:
  virtual ~RefactoredClass() { }; // Linking error if this is pure.  Why?
  virtual int foo() = 0;
};

class RefactorA : RefactoredClass {
  double data1, data2, data3, data4;

  public:
  int foo() { return 1; }
  ~RefactorA() { std::cout << "Destroying RefactorA" << std::endl; }
};

class RefactorB : RefactoredClass {
  int data;

  public:
  int foo () { return 2; }
  ~RefactorB() { std::cout << "Destroying RefactorB" << std::endl; }
};

// You may need to manually create copy, move, &ct operators for this.
// Requires C++11
union LegacyClass {
  RefactorA refA;
  RefactorB refB;

  LegacyClass(char type) { 
    switch (type) {
      case 'A':
        new(this) RefactorA;
        break;
      case 'B':
        new(this) RefactorB;
        break;
      default:
        // Rut-row
        break;
    }
  }

  RefactoredClass * AsRefactoredClass() { return (RefactoredClass *)this; }

  int foo() { return AsRefactoredClass()->foo(); }

  ~LegacyClass() { AsRefactoredClass()->~RefactoredClass(); }

};

int main (void) {
  LegacyClass A('A');
  LegacyClass B('B');

  std::cout << A.foo() << std::endl;
  std::cout << B.foo() << std::endl;

  return 0;
}

Somebody should have made an answer by now...so here's mine.

I'd recommend using a union of char array and one of the biggest integer types:

union {
    char refactored_class_buffer[ sizeof RefactoredClass ];
    long long refactored_class_buffer_aligner;
};

I also strongly recommend putting an assert or even an if(check) throw; into your factory so that you never, ever, exceed the size of your buffer.

If the data is the same for each case, and you're only changing behaviuor, you don't need to allocate in your core - this is basically a strategy pattern using singleton strategies. You end up using polymorphism in your logic, but not in your data.

class FooStrategy() {
virtual int foo(RefactoredClass& v)=0;
}

class RefactoredClass {
   int foo() {
     return this.fooStrategy(*this);
   }
   FooStrategy * fooStrategy;
};

class FooStrategyA : public FooStrategy {

   //Use whichever singleton pattern you're happy with.
   static FooStrategyA* instance() {
      static FooStrategyA fooStrategy;
      return &fooStrategy;
   }

   int foo(RefactoredClass& v) {
      //Do something with v's data
   }
}

//Same for FooStrategyB

Then when you create a RefactoredClass you set its fooStrategy to FooStrategyA::instance().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top