0
votes

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(), ...

2
Can you explain a bit more? What about stuff like MyClass<1>::foo<int, double>();? Do you intend to limit certain specializations of MyClass to only a single specialization of foo? - jrok
I do need stuff like MyClass<1>::foo<int, double>();. A limitation to a single specialization would mean, that I don't need a template class function at all. - user3497687
Then I don't understand why you explicitly specialized foo in your example. So, just to confirm, should MyClass<7>::foo<uint8_t, uint16_t>(); and MyClass<7>::foo<int, double>() do the same thing? - jrok
You questions are reasonable. I edited the question to make it more clear. Both specializations, if allowed, do different things. Probably it does not matter for a possible solution if foo is a template function or not. The key is the function specialization for the integer range. - user3497687

2 Answers

0
votes

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;
};
-1
votes

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.