ScarletAmaranth
ScarletAmaranth

Reputation: 5101

Refactoring inheritance into templates while retaining certain characteristics

I have the following structure:

class Base {
   virtual T foo() = 0;
};

class Derived : public Base {
   T foo() override { /**/ }
}

class Derived1 : public Base {
   T foo() override { /**/ }
}

I need the following to work (or an adequate substitute):

some_container<unique_ptr<Base>> objects;

Basically,
C++ AMP doesn't allow for virtual functions in kernels, but I definitely need an inheritance chain-like container behaviour.

What is a recommended / common pattern to transform this sort of inheritance chain to template magic?

Upvotes: 2

Views: 179

Answers (2)

Daniel Frey
Daniel Frey

Reputation: 56863

You could roll your own, manual vtable emulation:

class Base {
protected:
    using fp_t = int(*)(Base*);
    fp_t fp;
    Base( fp_t p ) : fp( p ) {}     
public:
    int foo() { return (*fp)(this); }
};

class Derived : public Base {
    static int sfoo(Base* b) { return static_cast<Derived*>(b)->foo(); }
    int foo() { return 42; }
public:
    Derived() : Base(&sfoo) {}
};

Live example

Upvotes: 0

Dan O
Dan O

Reputation: 4450

The canonical method to remove a vtable call is to replace it with a switch statement:

enum type_e
{
   type_derived,
   type_derived1
};

class Base
{
public:
   Base( type_e type ) : m_type( type ) {}

   T foo();

private:
   type_e m_type;
};


T Base::Foo()
{
   switch( m_type )
   {
   case type_derived:
      return //{...} your Derived::Foo()
   case type_derived1:
      return //{...} your Derived1::Foo()
   }
}

The only change in the API is that instead of call new Derived() you must call new Base( type_derived ). The major disadvantage is that you now have to hold all of your additional data (formerly members of Derived1 or Derived) in Base, which may bloat the class. On the other hand you can now make a container of Base by value, and avoid the overhead of std::unique_ptr.

Upvotes: 1

Related Questions