17

Title is a little ambiguous.

Lets say I have a template defined as:

template < typename T >
void foo ( int x ) ;
template <>
void foo<char> ( int x ) ;
template <>
void foo<unsigned char> ( int x ) ;
template <>
void foo<short> ( int x ) ;
...

Internally both foo<signed>() and foo<unsigned>() do exactly the same thing. The only requirement is that T be an 8bit type.

I could do this by creating another template to type define a standard type based on size.

template < typename T, size_t N = sizeof( T ) > struct remap ;
template < typename T, size_t > struct remap< 1 >
{
    typedef unsigned char value;
}
...

Note, function templates cannot have default parameters. This solution only relocates the problem to another template and also introduces a problem if somebody tried passing a struct type as a parameter.

What is the most elegant way to solve this without repeating those function declarations?

This is not a C++11 question.

dyp
  • 38,334
  • 13
  • 112
  • 177
Twifty
  • 3,267
  • 1
  • 29
  • 54
  • Have you tried using enable_if to check the size of the type? I'm assuming you're doing the same thing for all built-in types of the same size? – Borgleader Jul 13 '13 at 05:46
  • @Borgleader enable_if is C++11 – Twifty Jul 13 '13 at 05:49
  • 2
    SFINAE and [`enable_if`](http://en.cppreference.com/w/cpp/types/enable_if) can be implemented in C++03 as well. – dyp Jul 13 '13 at 05:52
  • @DyP I don't have access to the full C++11, so I've never used enable_if. How would it solve this problem? – Twifty Jul 13 '13 at 05:54
  • If I understand you correctly, you want to avoid code duplication. The simplest way I see is to call the same function from inside `foo` and `foo`. There are other ways similar to your `remap` suggestion, but you'd end up with more code to avoid the duplication, I think. – dyp Jul 13 '13 at 05:59
  • I think you mean `foo` and `foo`, right? `signed` and `unsigned` are `int`s, and those are at least 16 bit wide. Also note that `char`, `signed char` and `unsigned char` are **three distinct types**. – dyp Jul 13 '13 at 06:28

2 Answers2

13

One possibility is to specialize a class template for multiple types at once:

// from: http://en.cppreference.com/w/cpp/types/enable_if
    template<bool B, class T = void>
    struct enable_if {};

    template<class T>
    struct enable_if<true, T> { typedef T type; };

template < typename A, typename B >
struct is_same
{
    static const bool value = false;
};
template < typename A >
struct is_same<A, A>
{
    static const bool value = true;
};


template < typename T, typename dummy = T >
struct remap;

template < typename T >
struct remap
<
    T,
    typename enable_if<    is_same<T, unsigned char>::value
                        || is_same<T, signed char>::value, T >::type
>
{
    void foo(int);
};


int main()
{
    remap<signed char> s;
    s.foo(42);
}

Another possibility is to specialize a class template for categories of types (type traits):

#include <cstddef>

template < typename T >
struct is_integer
{
    static const bool value = false;
};
template<> struct is_integer<signed char> { static const bool value = true; };
template<> struct is_integer<unsigned char> { static const bool value = true; };


template < typename T, typename dummy = T, std::size_t S = sizeof(T) >
struct remap;

template < typename T >
struct remap
<
    T
    , typename enable_if<is_integer<T>::value, T>::type
    , 1 // assuming your byte has 8 bits
>
{
    void foo(int);
};


int main()
{
    remap<signed char> s;
    s.foo(42);
}
dyp
  • 38,334
  • 13
  • 112
  • 177
  • 1
    I just wanted to note there's a std::enable_if, so you don't have to define it yourself. See [here](http://en.cppreference.com/w/cpp/types/enable_if). – leetNightshade Mar 27 '15 at 17:45
  • 2
    @leetNightshade `enable_if` has been added in C++11; the question targets C++03 (also, I've already added a comment to the OP with the same link ;) – dyp Mar 27 '15 at 18:41
8

You need your remap trait to simply map from input types to output types, and have your foo<T>(int) interface function delegate to a foo_implementation<remap<T>::type>(int) implementation. i.e.:

template <typename T>
struct remap {
    // Default: Output type is the same as input type.
    typedef T type;
};

template <>
struct remap<char> {
    typedef unsigned char type;
};

template <>
struct remap<signed char> {
    typedef unsigned char type;
};

template <typename T>
void foo_impl(int x);

template <>
void foo_impl<unsigned char>(int x) {
    std::cout << "foo_impl<unsigned char>(" << x << ") called\n";
}

template <typename T>
void foo(int x) {
    foo_impl<typename remap<T>::type>(x);
}

See it live at ideone.com.

That said, it might be realistically simpler to define foo_char, foo_int and foo_short and just call the correct one from client code. foo<X>() isn't syntactically much different from foo_X().

Casey
  • 41,449
  • 7
  • 95
  • 125