Question

Is there any way to do a range specialize of template function in an integer template class? After a lot of research I get the feeling that the answer might be simply "no", but maybe there is a neat trick or workaround.

I simplyfied my class as much as possible to demonstrate the problem.

template<int value>
class MyClass
{
    template <typename T, typename X>
    uint64_t foo()
    {
        static_assert(sizeof(T) == 0, "Unsupported type combination.");
        return 0;
    }

    uint64_t m_member;
};

Very easy so far. The non-specialized implementation uses a static assertion, because I only want the template function to be used for certain types. These functions are specialized outside the class definition.

template<>
template<>
uint64_t MyClass<1>::foo<uint8_t, uint16_t>()
{
    return m_member * 1000;
}

template<>
template<>
uint64_t MyClass<7>::foo<uint8_t, uint16_t>()
{
    return m_member * 7000;
}

No I am looking for a solution to use this specialization for a certain range of val. I want MyClass<0> to throw the static assertion, MyClass<1>, MyClass<2>, ... , MyClass<6> should use MyClass<1> and finally MyClass<7> has to use the second specialization. Other specializations of foo work different. I have plenty of functions like foo and all do have about 10 type combinations working and all of them work very different.

The easiest workaround would be to simply duplicate the specializations, but I hope for a better solution. It would be nice if foo can be kept as a template function, but I also could live with a solution defining all possible combinations of foo, e.g. foo_u8_u16();, foo_u32_u64(), ...

Was it helpful?

Solution

This should work:

template<int value, class T, class X>
struct Helper {
    static uint64_t foo(uint64_t param) {
        return Helper<value - 1, T, X>::foo(param);
    }
};

template<class T, class  X>
struct Helper<0, T, X> {
    static uint64_t foo(uint64_t param) {
        static_assert(sizeof(T) == 0, "Unsupported type combination.");
        return 0;
    }
};

template<>
uint64_t Helper<1, uint8_t, uint16_t>::foo(uint64_t param) {
    return param * 1000;
}

template<>
uint64_t Helper<7, uint8_t, uint16_t>::foo(uint64_t param) {
    return param * 7000;
}

template<int value>
class MyClass {
    template<class T, class X>
    uint64_t foo() {
        return Helper<value, T, X>::foo(m_member);
    }

    uint64_t m_member;
};

OTHER TIPS

You can do this:

#include <type_traits>

// the implementation of foo is moved to a different class

template<int value, typename = void>
struct FooImpl
{
    template <typename T, typename X>
    uint64_t foo()
    {
        static_assert(sizeof(T) == 0, "Unsupported type combination.");
        return 0;
    }
};

// partial specialization that will be enabled
// when value is in range [1,6)

template<int value>
    typename =  typename std::enable_if<value > 0 && value < 7>::type>
struct FooImpl<value,
        typename std::enable_if<(value > 0 && value < 7)>::type>
{
    template <typename T, typename X>
    uint64_t foo()
    {
        return 1000;
    }
    // you can write several overloads instead of a template, too
};

// now inherit from FooImpl

template<int value>
class MyClass : private FoomImpl<value>
{
    // write a forwarding function for foo here
    // or just a using declaration
};

Hope that helps and that I understood your question correctly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top