lundi 22 décembre 2014

Enforcing object types in pipeline using templates and polymorphism in c++

I am trying to come up with a generic pipeline-type pattern with the following rules:


1). Have a head, filter and mapper classes with the possible connection head -> filter -> filter -> ... -> mapper (however, I have no intention of enforcing this for now). I have no intention to introduce feedback loops, but would like the design to be, at least, extendable, to cases with multiple segments connected to the same resource.


2). The types of classes that can be connected together are enforced via template parameters. The template parameter passed to the class should be a base class for all classes that can be interfaced with the current class (this is where the main problem lies).


3). Each filter and head segment have two clearly defined execution stages: active and passive. The 'active' stage must be performed upon request from the object downstream the pipeline. After the active stage, the object enters into the passive state, where it can output the data upon request from the object downstream the pipeline, but cannot modify its members.


4). The interfaces between the classes must not be enforced in any way beyond the types of classes that can be joined together.


The original plan was to do this using a simple template techniques. Here is an example of a Filter class (the mapper and reader are very much the same). In this case TInputConnectionType provides the type of the input connection (which can be a base class). The TClassType provides the type of the derived class. processConnectionOutput is the method that is responsible for the 'active' stage of execution of the segment. At this stage the members of the class can be modified.


The idea is that after the execution of the active stage, the object passes the const pointer to itself downstream the pipeline. Thus, it is now in the 'passive' stage and the next segment downstream the pipeline can request information from it via a defined interface.



template<typename TInputConnectionType, typename TClassType>
class Filter : public Process
{

protected:
std::shared_ptr<TInputConnectionType> p_InputConnection;
public:
virtual void setInputConnection(
std::shared_ptr<TInputConnectionType> c_p_InputConnection
)
{
p_InputConnection = c_p_InputConnection;
}
virtual std::shared_ptr<TClassType const> execute(void)
{
this -> openConnection();
this -> processConnectionOutput(
this -> p_InputConnection -> execute()
);
p_InputConnection -> clean();
this -> closeConnection();

return std::shared_ptr<TClassType const>
(static_cast<TClassType*>(this));
}
virtual void processConnectionOutput(
std::shared_ptr<TInputConnectionType const>
) = 0;
};


Unfortunately, this only works if the classes passed as the template parameters are the actual classes that are being connected together and not the base classes. The bottleneck lies in the down casting of the "this" pointer. I am wondering if there are any ways the pointer to const derived class can be passed on down the pipeline without having to explicitly cast it. The preference is for the smart pointers.


Note: shared_ptr is used as, in general, there may be several connections to the object. Plus, the object may be pointed to at the outer layer. There also may be a need to destroy the object and redirect the pointer, so return by const reference is also not suitable.


I also welcome any suggestions for out-of-the-box solutions that will work in my case, perhaps relying on a different pattern.


Aucun commentaire:

Enregistrer un commentaire