1

I want to create a simple integer range checker and converter using c++ templates.
The code looks like this:

// D is the "destination" type and S the "source" type
template <class D, class S>
inline D SafeConvert( S value );

template <class S>
inline int SafeConvert<int>( S value ) {

    ASSERT( value >= S(INT_MIN) && value <= S(INT_MAX) );
    return int(value);
} /// error C2768: 'SafeConvert' : illegal use of explicit template arguments


template <class S>
inline size_t SafeConvert<size_t>( S value ) {

    ASSERT( value >= S(0) && value <= S(size_t(-1)) );
    return size_t(value);
} /// error C2768: 'SafeConvert' : illegal use of explicit template arguments


// ...

void test() {

    size_t v = INT_MAX+1;
    int iv = SafeConvert<int>(v);
}

However I have the following come compilation errors:

error C2768: 'SafeConvert' : illegal use of explicit template arguments

My question is how to tell the compiler that I want to specialize only the D class ?

Thanks.

Franck Freiburger
  • 26,310
  • 20
  • 70
  • 95

4 Answers4

5

You can't partially specialize function templates. You need to mimic it with a class wrapper or use standard function overloading. An example of mimicing:

template <typename T1, typename T2>
struct processor;

template < typename T1, typename T2 >
T1 fun(T2 t2) { return processor<T1,T2>::apply(t2); }

template < typename T2 >
struct processor<int,T2>
{
   static int apply(T2 t2) { .... }
};

...etc...
Edward Strange
  • 40,307
  • 7
  • 73
  • 125
4

It's going to be a bother, and a hell to maintain.

Normally I would advise using the numeric_limits:

template <class D, class S>
D SafeConvert(S value)
{
  ASSERT(value >= std::numeric_limits<D>::min()
      && value <= std::numeric_limits<D>::max());
  return static_cast<D>(value);
}

However there is a warning emitted by the compiler whenever you compare a signed integer with an unsigned one... (never really understood this by the way)

So, instead of reinventing the wheel, I shall advise the use of Boost.NumericConversion and notably: boost::numeric_cast<>.

It's guaranteed to be performance free when the check is not required (ie the destination type is bigger than the source type) and otherwise perform the necessary checks.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Numeric limits and boost::numeric_cast is the way to go. – Puppy Jun 24 '10 at 21:23
  • Does that assert work both in situations where the max of S is bigger and when the max of D is bigger? Or will implicit conversions screw it up in one direction? – Joseph Garvin Jul 29 '12 at 17:56
  • @JosephGarvin: I am pretty confident that `boost::numeric_cast` works ;) For the custom solution presented here, I am afraid I don't know the implicit conversions by heart. – Matthieu M. Jul 30 '12 at 07:07
1

Write a structure SafeConverter<T, S> that is used by SafeConvert. Better than partial specialization would be using std::numeric_limits, or even boost::numeric_cast, which already implements range checking in a more sophisticated way.

The latter could be implemented as follows:

template<typename T, typename S>
struct numeric_converter {
  static T convert(const S& val);
}
template<typename T, typename S>
T numeric_cast(const S& val) {
  typedef numeric_converter<T, S> converter;
  return converter::convert(val);
}
Philipp
  • 48,066
  • 12
  • 84
  • 109
  • 1
    I won't neg since I almost said the same thing but you should have checked your answer first. Then you might have remembered why you can't do it that way. – Edward Strange Jun 24 '10 at 16:04
  • the problem with SO is that the answers pop up way too fast so that I usually have no time to think about my answer before posting, sorry. – Philipp Jun 24 '10 at 16:10
0

Just write SafeConvert<size_t, S> instead of SafeConvert<size_t>, I think, to specialise only the second parameter. Noah Roberts is correct, too, on the point of partial specialisation of functions versus types.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166