0
votes

I am trying to design a pipeline for my project. I am loosely relying on the VTK pipeline concept. However, there are major differences.

In my design the input-output connection type matching was done using variadic templates and recursive inheritance (similar to CRTP). This allows me to manually define which segments can be connected with which segments by passing a list of abstract base classes to the base filter/mapper classes. In itself, this does not cause any problems.

I need to be able to work with custom (not necessarily std/primitive) data types. The VTK pipeline, passes pointers to objects that derive from one of the VTK classes (vtkAlgorithmObject) through pipeline segments. This allows quite a natural implementation of the pipeline input/output interface with multiple connections. This is where my problem lies.

The VTK implementation of the output port function is:

vtkAlgorithmOutput* vtkAlgorithm::GetOutputPort (int index) 

Unfortunately, I cannot return a custom class from the output port. Somehow, I need to have a single (possibly overloaded) function getOutputPort that will return a different type based on the pre-defined index.

One way of doing this is using a combination of template arguments and overloading:

template<int N>
void getOutputPort(int& i)
{
    if (N == 0)
        i = 10;
    else if (N == 2)
        i = 20;
    else
        throw "Invalid template argument for the return type.";
}

template<int N>
void getOutputPort(string& str)
{
    if (N == 1)
        str = "qwerty";
    else
        throw "Invalid template argument for the return type.";
}

However, apart from the obvious problem of splitting the definition and index selection over multiple functions, I have to return by reference, which is not something that I would prefer to do. At the concept, I would like to pass the objects via std::unique_ptr. This will ensure that two segments downstream the pipeline will never make attempts to use the same resource.

Thus, I am trying to do something similar to what is shown in the code below

template<int N>
auto getOutputPort2()
{
    if (N == 0)
        return std::unique_ptr<int>(new int(10));
    else if (N == 1)
        return std::unique_ptr<string>(new string("qwerty"));
    else if (N == 2)
        return std::unique_ptr<int>(new int(20));
    else
        throw "Invalid template argument.";
}

For obvious reasons, this results in the error 'inconsistent deduction for auto'. Are there any ways around the problem (not necessarily using auto)?

2
Explicit specialization?=Columbo

2 Answers

1
votes

You may do something like:

template<int N> struct helper_type {
    static_assert(N == 0 || N == 1 || N == 2, "Unexpected value");
    using type = typename std::conditional<N == 1,
        std::unique_ptr<std::string>,
        std::unique_ptr<int>>::type;
};

/* only specializations are valid */
template<int N>
typename helper_type<N>::type getOutputPort2() = delete; 

template<>
typename helper_type<0>::type getOutputPort2<0>()
{
    return std::unique_ptr<int>(new int(10));
}

template<>
typename helper_type<1>::type getOutputPort2<1>()
{
    return std::unique_ptr<std::string>(new std::string("asdf"));
}

template<>
typename helper_type<2>::type getOutputPort2<2>()
{
    return std::unique_ptr<int>(new int(20));
}
0
votes

Thank you for the comments and answers. Unfortunately, accidentally, I have not included certain important information in the statement of the problem. getOutputPort2() was supposed to be a member of a class that represents a pipeline segment. However, I did not state explicitly that the class would be a template. Thus, the solutions that are based on the explicit specialisation are not applicable, because it is necessary to specialise both the class and the member function template.

The solution that I decided to use is based on one of the answers to the following question: explicit return type specialisation question. Please see the code below.

template <typename T>
class A
{

public:

    // the Get Value we want
    template <int N>
    auto getValue()
        {
            return get_value_impl<N>::apply(*this);
        }

    // the general get value struct
    template<int N, typename = void>
    struct get_value_impl
    {
        static auto apply(A a)
            {
                throw "Invalid template argument.";
            }
    };

    // partial specialization 1
    template <typename S>
    struct get_value_impl<0, S>
    {
        static std::unique_ptr<double> apply(A a)
            {
                return std::unique_ptr<double>(new double(10.0));
            }
    };

    // partial specialization 2
    template <typename S>
    struct get_value_impl<1, S>
    {
        static std::unique_ptr<string> apply(A a)
            {
                return std::unique_ptr<string>(new string("Hello world!"));
            }
    };

};