Date: prev next · Thread: first prev next last
2012 Archives by date, by thread · List index


Background: we have in sc/source/filter/inc/ftools.hxx supposedly
"safe" casts that are (according to the Doxygen documentation)
supposed to be safer than just static_cast. As fdo#39589 uncovered,
they are actually so buggy as to be *less* safe.

Back in November, I wrote a prototype _actually_ safe versions, but
got sidetracked and forgot about them. I'm unearthing them again now,
attached.

The big remaining issue is that it is a WaE disaster on "warning:
comparison between signed and unsigned", because the compiler does not
notice that these comparisons are done under scope of if/else
conditions that make them safe. Also "warning: test will always be
true/false", since the code covers all cases and not all cases make
sense for all combinations of A,B in "cast a value of type A to type
B": the idea is for the compiler to prune away if/else branches that
will not be hit.

The "comparison between signed and unsigned" warnings cannot be easily
silenced by explicit casts, because the code makes use of the
compiler's automatic 'the smaller type is promoted to the bigger type'
rules. We could instead reimplement that logic manually, but I'm
rather loathe on making the code more complicated just to avoid
spurious warnings.

So maybe we could selectively disable the warnings on this file
instead? Someone knows how to do that with gcc and with MSVC?

As I wrote in November:

That thing is devilishly hard to get right... I think I have a
correct version now, (...)

But IMHO it needs to be unittest-ed deeply before commit.

-- 
Lionel
#include <X11/Xlibint.h>
#include <limits>
#include <memory>

#include <stdio.h>

// assumptions:
// \forall typename Type:
//     ( (::std::numeric_limits< Type >::min() == 0) XOR ( ::std::numeric_limits< Type >::min() < 0 
 && ::std::numeric_limits< Type >::is_signed ))
// &&  (::std::numeric_limits< Type >::max() > 0 )

// signed unbound types are unbound towards both infinities

template< typename ReturnType, typename Type >
inline ReturnType ulimit_cast( Type nValue, ReturnType nMax )
{
  if ( ! ::std::numeric_limits< ReturnType >::is_signed && ::std::numeric_limits< Type >::is_signed 
&& nValue < 0 )
    // nValue cannot be represented in ReturnType -> return nMax
    // We could have returned 0, but "ulimit_cast (Type nValue)" depends on this behaviour
    return nMax;
  else if ( nMax < 0 && nValue >= 0 )
    return nMax;
  else if ( ! ::std::numeric_limits< ReturnType >::is_bounded )
    return ::std::min<ReturnType>(static_cast<ReturnType>(nValue), nMax);
  // Due to implicit type conversion, the comparison in the if occurs in the type
  // that has the biggest ::max()
  // -> safe, because case (nMax < 0 && Type is unsigned) is already excluded
  else if ( nMax >  ::std::numeric_limits< Type >::max() )
    // nValue <= ::std::numeric_limits< Type >::max() < nMax,
    // and it is unsafe to compare nMax to nValue within Type:
    // nMax would be converted with loss
    return static_cast < ReturnType >(nValue);
  else
    // nMax fits in Type; safe to compare to nValue within Type
    return static_cast< ReturnType >(::std::min<Type>(nValue, nMax));
}

/* if ( (nValue < 0) && (nValue < ::std::numeric_limits< ReturnType >::min()) )
     return nMax
   else
     return cast(min<Type> ( nValue, nMax ))
 */


template< typename ReturnType, typename Type >
inline ReturnType ulimit_cast( Type nValue )
{
  return ulimit_cast( nValue, ::std::numeric_limits< ReturnType >::max() );
}

template< typename ReturnType, typename Type >
inline ReturnType llimit_cast( Type nValue, ReturnType nMin )
{ return static_cast< ReturnType >( ::std::max< Type >( nValue, nMin ) ); }

template< typename ReturnType, typename Type >
inline ReturnType llimit_cast( Type nValue )
{ return llimit_cast( nValue, ::std::numeric_limits< ReturnType >::min() ); }

template< typename ReturnType, typename Type >
inline ReturnType limit_cast( Type nValue, ReturnType nMin, ReturnType nMax )
{
  // First two cases: problematic cases in signed<->unsigned casts
  if ( nMin >= 0 && nValue < 0 )
    return nMin;
  else if ( nValue >= 0 && nMax < 0 )
    return nMax;
  else if ( ! ::std::numeric_limits< ReturnType >::is_bounded )
    return ::std::min<ReturnType>(::std::max(static_cast<ReturnType>(nValue), nMin), nMax);
  else
  {
    // We first take care of nMax
    /* comparison safe because (nMax < 0 && Type unsigned) already handled */
    if ( nMax <= ::std::numeric_limits< Type >::max() )
    {
      if ( nMax < 0 &&
           /* Type is necessarily signed */
           nMax < ::std::numeric_limits< Type >::min() )
        // nValue >= ::std::numeric_limits< Type >::min() > nMax
        return nMax;
      else
        // safe to compare within Type
        nValue = ::std::min< Type >( nValue, static_cast< Type >(nMax) );
    }
    /* else nValue <= ::std::numeric_limits< Type >::max() <= nMax */
    // Now nValue <= nMax
    // We take care of nMin
    if ( nMin >= 0 )
    {
      // nValue >= 0 because nMax >= nMin >= 0 and case (nValue < 0 && nMin >=0) is already handled
      // thus no signedness issues
      if ( nMin <= ::std::numeric_limits< Type >::max() )
        nValue = ::std::max< Type >( nValue, static_cast< Type >(nMin) );
      else
        // nValue <= ::std::numeric_limits< Type >::max() < nMin
        return nMin;
    }
    else if ( nValue >= 0 )
      // nValue >= 0 > nMin
      return static_cast < ReturnType > (nValue);
    // both Type and ReturnType are unsigned
    else if ( nMin < ::std::numeric_limits< Type >::min() )
      // nMin < ::std::numeric_limits< Type >::min() < nValue
      return static_cast< ReturnType > (nValue);
    else
      // safe to compare
      nValue = ::std::max< Type >( nValue, static_cast< Type >(nMin) );

    return static_cast< ReturnType >(nValue);
  }
}
 
template< typename ReturnType, typename Type >
inline ReturnType limit_cast( Type nValue )
{ return limit_cast( nValue, ::std::numeric_limits< ReturnType >::min(), ::std::numeric_limits< 
ReturnType >::max() ); }

int main(int argc, char *argv[])
{
  int foo = (1l << 31) - 1 ;
  printf("foo: %d\nstatic_cast: %u\nulimit_cast: %u\nlimit_cast: %u\n", foo, static_cast<unsigned 
int>(foo), ulimit_cast<unsigned int>(foo), limit_cast<unsigned int>(foo));
}

Context


Privacy Policy | Impressum (Legal Info) | Copyright information: Unless otherwise specified, all text and images on this website are licensed under the Creative Commons Attribution-Share Alike 3.0 License. This does not include the source code of LibreOffice, which is licensed under the Mozilla Public License (MPLv2). "LibreOffice" and "The Document Foundation" are registered trademarks of their corresponding registered owners or are in actual use as trademarks in one or more countries. Their respective logos and icons are also subject to international copyright laws. Use thereof is explained in our trademark policy.