jeudi 28 mai 2015

C++ Class inheritance design choice for composite classes


Problem Background

I need to represent trajectory points and trajectories (encapsulating these points) in the form of a collection of classes. All trajectory points have two essential elements: time and state. A trajectory is an ordered set of trajectory points.

The time can be discrete or continuous, but, more generally, I would like to ensure that an arbitrary data type (e.g. float or double) can be used for storage of the information about time. The state should also be of an arbitrary type (e.g. a float or std::vector<std::complex<int>>).

Nevertheless, both for the time and state there exist certain data types which are of interest. Moreover, I may need to provide specific functionality for a combination of data types for the time and state. For example, I may need to provide an interface to simple data analysis/export type based on the type of state or time or a combination of thereof.

The problem described above also applies to the trajectory classes which should store the instances of the trajectory points in the form of an std::vector<std::unique_ptr<TrajectoryPointType>>. Ideally, there should be a base trajectory class that provides the core functionality for any type of a trajectory point and a set of specialised classes that operate on concrete types of trajectory points.

My current implementation seems to be intuitive. However, I am concerned that there may be a more efficient way of doing what I am trying to achieve through the use of established design patterns that are unknown to me.


Current Implementation

In the current implementation, I have a base class template for the definition of the trajectory points (DSTrajectoryPoint) and a base class template for the definition of the trajectories (Trajectory). The class DCTrajectoryPoint accepts two template arguments: TTimeType and TStateSpaceType.

At the second level of the inheritance, the classes that derive from the class DSTrajectoryPoint would, typically, derive by providing either a concrete type for TTimeType or a concrete type for TStateSpace. Thus, for example, at the second level I could have a class

DSTrajectoryPointCT: public DSTrajectoryPoint<double, TStateSpace>

(represents points with continuous time) or a class

DSTrajectoryPointCn: DSTrajectoryPoint<TTimeType, std::vector<std::complex<int>>>

(represents points whose state is a vector of complex numbers).

At the third level, the classes use multiple inheritance to derive from a class with a concrete type for the time and a concrete type for space. Thus, for example, at the third level, I could have a class

DSTrajectoryPointCTCn: public DSTrajectoryPointCT<std::vector<std::complex<int>>>, public DSTrajectoryPointCn<double>

The trajectory classes would be structured in a similar way. However, there is an additional complexity. The trajectory class needs to know both the type of the trajectory point and the types of the time and state associated with the trajectory point. Thus, the base trajectory class template accepts three template parameters, i.e. DSTrajectory<TTimeType, TStateSpaceType, TTrajectoryPointType>.

The inheritance diagram for the classes that derive from DSTrajectory would look similar to the inheritance diagram for the DSTrajectoryPoint. The intention is to have a trajectory class (or class template) corresponding to each trajectory point.

#include <iostream>
#include <complex>
#include <memory>
#include <vector>

class Base
{
public:
    virtual ~Base(void) = 0;
};
Base::~Base(void){}

template<typename TTimeType, typename TStateSpaceType>
class DSTrajectoryPoint : public Base
{
protected:
    TTimeType TTimeTypeInstance;
    std::unique_ptr<TStateSpaceType> u_TStateSpaceTypeInstance;
public:
    DSTrajectoryPoint(){}
    virtual ~DSTrajectoryPoint(void){}
};

template<typename TStateSpaceType>
class DSTrajectoryPointCT:
    virtual public DSTrajectoryPoint<double, TStateSpaceType>
{
public:
    using DSTrajectoryPoint<double, TStateSpaceType>::DSTrajectoryPoint;
    virtual ~DSTrajectoryPointCT(void)
        {}
};

template<typename TTimeType>
class DSTrajectoryPointCn:
    virtual public DSTrajectoryPoint<TTimeType, std::vector<std::complex<int>>>
{
public:
    using DSTrajectoryPoint<
    TTimeType, std::vector<std::complex<int>>
    >::DSTrajectoryPoint;
    virtual ~DSTrajectoryPointCn(void){}
};

class DSTrajectoryPointCTCn :
    public DSTrajectoryPointCT<std::complex<int>>,
    public DSTrajectoryPointCn<double>
{
public:
    DSTrajectoryPointCTCn(void):
        DSTrajectoryPoint<double, std::complex<int>>(),
        DSTrajectoryPointCT<std::complex<int>>(),
        DSTrajectoryPointCn<double>()
        {};
};

template<
    typename TTimeType,
    typename TStateSpaceType,
    class TDSTrajectoryPointType
    >
class Trajectory: public Base
{
protected:
    std::vector<std::unique_ptr<TDSTrajectoryPointType>> m_Trajectory;
public:
    static_assert(
        std::is_base_of<
        DSTrajectoryPoint<TTimeType, TStateSpaceType>,
        TDSTrajectoryPointType
        >::value,
        "Error: TDSTrajectoryPointType must derive"
        " from DSTrajectoryPoint"
        );
    Trajectory(void){}
    virtual ~Trajectory(void){}
};

int main()
{
    Trajectory<
        double,
        std::complex<int>,
        DSTrajectoryPointCTCn
        > T;
}

Aucun commentaire:

Enregistrer un commentaire