Advanced CRTP#

Polaris uses some advanced techniques in order to implement polymorphic behavior and yet maintain excellent performance.

The Curiously-Recurring-Template-Pattern (CRTP) is used to allow polymorphic behavior be defined at compile time as opposed to runtime. This allows the CPU to predict what code is required for each method and does not need to load code into the local execution cache at the last moment. CRTP does cause some complications however. Containers need to be homogenous and inheritance can be tricky. Since the objects in a container are not runtime polymorphic other techniques must be used. We currently use callback functions to handle this. Another alternative is the use of variants which were introduced in C++11. Examples of this will be added to this wiki at a later date.

Deriving Polaris objects from other Polaris objects#

See the code in apps/inheritance_example the repository for a fully functional Polaris based application that demonstrates inheritance, meta-programming and SFINAE (Substitution Failure Is Not An Error) techniques.

A base object in Polaris would look something like this:

implementation struct Agent_Implementation : public Polaris_Component<MasterType, INHERIT(Agent_Implementation), Execution_Object>, public Agent<IMPLTYPE(Agent_Implementation)>
{
  impl_func(Agent_Implementation, void, DoStuff);
};

An object derived from this would look like:

implementation struct New_Agent_Implementation : public Agent_Implementation<MasterType, INHERIT_IMPL(New_Agent_Implementation)>
{
  impl_func(New_Agent_Implementation, void, DoStuff);
};

When objects are instantiated the impl_func() macro determines which version of the method to use by walking down the inheritance chain.

Agent_Implementation<MasterType>* base = Allocate<Agent_Implementation<MasterType>();
New_Agent_Implementation<MasterType>* derived = Allocate<New_Agent_Implementation<MasterType>();

base->DoStuff(); // finds the base DoStuff - nothing more in chain so uses it
derived->DoStuff(); //Finds base method - then derived method - so uses derived method

There is however a problem when there are gaps in the chain. Suppose you have a base object, an object derived from that and then another object derived from that.

implementation struct Base : public Polaris_Component<MasterType, INHERIT(Base), Execution_Object>
{
  impl_func(Base, void, DoStuff);
};
implementation struct Derived1 : public Base<IMPLTYPE(Derived1)>
{
};

implement void IMPLTYPE(Derived1)::_DoStuff(){ static_cast<basetype*>(this)->_DoStuff();}
implementation struct Derived2 : public Derived1<IMPLTYPE(Derived2)>
{
  impl_func(Base, void, DoStuff);
};

Note that Derived1 does not define a method for DoStuff(). If you instantiate Derived1 and call DoStuff() it will execute the method from Base. If you instantiate Derived2 and call DoStuff() - it will also execute the code from Base. This is because the chain for the DoStuff() method is broken.

The best way to correct this is to define a method in Derived1 that calls the method from Base.

implementation struct Base : public Polaris_Component<MasterType, INHERIT(Base), Execution_Object>
{
  impl_func(Base, void, DoStuff);
};
implementation struct Derived1 : public Base<IMPLTYPE(Derived1)>
{
  #define basetype Agent_Implementation<MasterType,INHERIT_IMPL(Derived1)>
  impl_func(Derived1, void, DoStuff);
};

// Inthe methods file create
implement void IMPLTYPE(Derived1)::_DoStuff(){ static_cast<basetype*>(this)->_DoStuff();}

implementation struct Derived2 : public Derived1<IMPLTYPE(Derived2)>
{
  impl_func(Base, void, DoStuff);
};

Now the chain is complete. You should only be required to do this when there is more than 2 derivations and one of the derivations in the middle does not define a method you want to override in a later derivation.