tags:

views:

120

answers:

4

Hello,
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.

+1  A: 

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
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.
Noah Roberts
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
+5  A: 

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...
Noah Roberts
Or just use a library.
Matthieu M.
A: 

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
Other way round; you want to specialise the first (destination) argument.
Mike Seymour
@Mike: Thanks, fixed.
Jon Purdy
+3  A: 

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.
Numeric limits and boost::numeric_cast is the way to go.
DeadMG