vendredi 1 mai 2015

C++ CRTP based dataflow output class design simplification


Background information

I am working on a dataflow-like design pattern. The two classes presented below are meant to represent an output data dispatch mechanism. level1 is a CRTP base class. getOutput<N> in level1 is the function that can be used for getting the output data from an instance of a derived class. Depending on the template parameter N it calls one of the user defined getOutputImpl functions. These functions are meant to be provided in a (CRTP-style) derived class. Each of the getOutputImpl defines an output port associated with the user defined derived class. The input type of the getOutputImpl is defined by design. The output type of the getOutputImpl methods can vary. However, by design, the output type must have a structure std::unique_ptr<TOutputType>, where TOutputType can be any class. More background information can be found here: previous question.


Question

To allow for automatic recognition of the number of the user defined ports (i.e. getOutputImpl methods) a method getOutputPortsNumber(void) is provided in the base level1 class. This method is based around the idea that the return type of all user defined getOutputImpl functions is std::unique_ptr<TOutputType>. Thus, one can define an additional getOutputImpl method in the base class that does not have this return type (e.g. it has a void return type: void getOutputImpl(...)).

The methodology described above works if the void getOutputImpl(...) is defined within the user-defined derived class (DataflowOutputClass in this example) along with other user-defined std::unique_ptr<TOutputType> getOutputImpl(...) methods. However, when the additional void getOutputImpl(...) method is moved to the base level1 class, I get a compilation error: no matching function for call to 'DataflowOutputClass<int>::getOutputImpl(PortIdxType<2ul>, const PolyIndex&) const.


Code

typedef size_t Index;
typedef unsigned long Natural;
typedef std::vector<Index> PolyIndex;
typedef const PolyIndex& crPolyIndex;
template<Index N> struct PortIdxType{};

template<typename TLeafType>
class level1
{

public:

    TLeafType* asLeaf(void)
        {return static_cast<TLeafType*>(this);}

    TLeafType const* asLeaf(void) const
        {return static_cast<TLeafType const*>(this);}

    template <Index N>
    auto getOutput(crPolyIndex c_Idx) const
        {return asLeaf() -> getOutputImpl(PortIdxType<N>{}, c_Idx);}

    static constexpr Natural getOutputPortsNumber(void)
        {return getOutputPortsNumberImpl<0>();}

    template<Index N>
    static constexpr std::enable_if_t<
        std::is_void<
            decltype(
                std::declval<TLeafType*>() ->
                getOutput<N>(PolyIndex({}))
                )
        >::value,
            Index
            > getOutputPortsNumberImpl(void)
        {return N;}

    template<Index N>
    static constexpr std::enable_if_t<
        !std::is_void<
            decltype(
                std::declval<TLeafType*>() ->
                getOutput<N>(PolyIndex({}))
                )
        >::value,
            Index
            > getOutputPortsNumberImpl(void)
        {return getOutputPortsNumberImpl<N + 1>();}

    template<Index N>
    void getOutputImpl(
        PortIdxType<N>, crPolyIndex c_Idx
        ) const
        {throw std::runtime_error("Wrong template argument.");}


};

template<typename T>
class DataflowOutputClass:
    public level1<DataflowOutputClass<T>>
{
public:

    // if void getOutputImpl(...) const is moved here from level1,
    // then the code compiles and works correctly.

    //overload for when N = 0
    std::unique_ptr<double> getOutputImpl(
        PortIdxType<0>, crPolyIndex c_Idx
        ) const
    {
        std::unique_ptr<double> mydouble(new double(10));
        return mydouble;
    }

    //overload for when N = 1
    std::unique_ptr<int> getOutputImpl(
        PortIdxType<1>, crPolyIndex c_Idx
        ) const
    {
        std::unique_ptr<int> myint(new int(3));
        return myint;
    }

};


int main()
{
    DataflowOutputClass<int> a;
    std::cout << a.getOutputPortsNumber() << std::endl;
}

Aucun commentaire:

Enregistrer un commentaire