Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2004-02-17 16:50:53


Hi All,

Michael Borghart is helping me fix some extrange problem with the numeric
conversion lib on VC7.0
The problem doesn't exist on VC7.1 and it is not a compilation problem (at
least not one that produces a compile fail).

If anyone with VC7.0 can help me here I'll appreciate it,
TIA

The problem can be seen with the following simple test:

#include<boost/numeric/converter.hpp>

int main()
{
  long l = -1 ;
  boost::numeric_converter<unsigned long,long>::convert(l);
}

that code is supposed to raise an exception at runtime because -1 is out of
range for "unsigned long".
On VC7.0, however, it doesn't.

For you to help me, you'll have to step into the code to see what is it
doing,
Here's a description of the code and what it is should happen:

There are two main kinds of converters: rounding and non-rounding:
The rounding converter is used ONLY for float to integral conversions.
Their conversion entry code looks like:

    static result_type convert ( argument_type s )
    {
      RangeCheckerBase::validate_range(s);
      source_type s1 = Float2IntRounderBase::nearbyint(s);
      return RawConverterBase::low_level_convert(s1);
    }

And for non-rounding converters (used by anything but float to int):

    static result_type convert ( argument_type s )
    {
      RangeCheckerBase::validate_range(s);
      return RawConverterBase::low_level_convert(s);
    }

There you can see the different "steps" of the conversion process.

First, validate_range() performs the range checking.
If this function returns (which shouldn't in case of an out of range),
low_level_convert() is called to perform the actual conversion.

low_level_convert() is supplied by the user as part of the
RawConverter policy, but validate_range() is selected at compile
time accordining the the combined properties of the
Source and Target types.

In our case, it appears that VC7.0 is choosing the wrong validate_range()
implementation.

Unless Source==Target, validate_range() calls the function out_of_range()
which looks like:

    static range_check_result out_of_range ( argument_type s )
    {
      typedef typename combine<IsNegOverflow,IsPosOverflow>::type
        Predicate ;

      return Predicate::apply(s);
    }

Here you can see that the actual checking is performed by a "predicate"
which is composed at compile time.
Depending on the type properties, the predicate might be "combined", meaning
that two cascading tests are performed instead of just one.
If for a given case only a single predicate is used, the code should step
into a function of the form:

      static range_check_result apply ( argument_type s )
      {
          return {...some specific test....}
      }

Otherwise, the "outer" apply simply calls two applies in a row:

      static range_check_result apply ( argument_type s )
      {
        range_check_result r = PredA::apply(s) ;
        if ( r == cInRange )
          r = PredB::apply(s);
        return r ;
      }
    } ;

Finally, each individual "apply" is implemented by the set of objects
that appear at the top of "detail\converter.hpp"
These predicates perform the minimally neccesary testing to decide if the
source value is out of range.

For this particular conversion: long->unsigned long
The only required test is (s<0), so the predicate that should be selected by
the metaprogramming is the class: LT_Zero

I suspect that VC7.0 is using a different predicate.

So, if you could step inside

validate_range()
  out_of_range()
     apply()
        apply() // only if it stepped into the "combiner" predicate

and print the stack from there I'll see what's happening.

TIA

Fernando Cacciola
SciSoft


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk