$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [Safe Numerics] Review
From: John Maddock (jz.maddock_at_[hidden])
Date: 2017-03-12 19:11:04
On 12/03/2017 15:56, Robert Ramey via Boost wrote:
> On 3/12/17 7:54 AM, Peter Dimov via Boost wrote:
>> John Maddock wrote:
>>
>>> 5) What is the purpose of class safe_literal? constepxr initialization
>>> seems to work just fine without it?
>>
>> The idea, I believe, is that if you have
>>
>>    safe_signed_range<0, 100> x;
>>
>> and then you do
>>
>>    auto y = x * safe_signed_literal<2>();
>>
>> you get safe_signed_range<0, 200> as the type of y. This could probably
>> be made less elaborate with a user-defined literal, for example
>>
>>    auto y = x * 2_sf;
>>
>> or something like that.
That would be my preferred solution - to define a user-defined suffix, 
so that:
4_safe
is a literal of type safe_signed_range<4,4>.  No need for safe_literal 
here IMO?
>>
>> Since x is not constexpr, it's not possible (I think) to achieve this
>> result without using a separate literal type to hold the compile-time
>> constant 2.
>
> here is the problem:
>
> constexpr int i = 42;   // i is constexpr and available at compile time
> constexpr const safe_int x(i); // x is constexpr and available at 
> compile time
>
> constexpr const safe_int y(42);  // y is NOT available at compile time!!!
Oh yes it is!
There is nothing at all non-constexpr about integer literals. Consider 
the following toy class, that restricts values to the range [-10,10], it 
performs runtime checking just like your safe class does, and still 
works in constexpr:
class ten
{
private:
     int m_value;
public:
     template <class I>
     constexpr ten(I i) : m_value(i)
     {
         if (i > 10 || i < -10)
             throw std::overflow_error("Expected value excedes +-10");
     }
     constexpr ten(const ten& a) : m_value(a.m_value) {}
     constexpr ten& operator += (const ten& i)
     {
         if ((i.m_value > 0) && (m_value > 0) && (10 - m_value < i.m_value))
             throw std::overflow_error("Addition results in value > 10");
         if ((i.m_value < 0) && (m_value < 0) && (10 + m_value > i.m_value))
             throw std::overflow_error("Addition results in a value < 10");
         return *this;
     }
     explicit constexpr operator int()const { return m_value; }
};
constexpr ten operator+(const ten& i, const ten& b)
{
     ten result(i);
     return result += b;
}
Now given:
template <int value>
struct ten_holder {};
we can write:
         const constexpr ten i(5);
         const constexpr ten j(5);
         const constexpr ten k = j + i;
         ten_holder<static_cast<int>(k)> holder;
And it all just works, of course anything that results in ten holding an 
out of range error triggers a compiler error because then a code path 
that can't be executed at compile time (like a throw) is triggered.
In fact safe is partly working too:
         constexpr const boost::numeric::safe<int> sy(42);
         ten_holder<static_cast<int>(sy)> holder3;  //OK sy is available 
at compile time.
It's the arithmetic operators that are broken somewhere..... ah... FOUND IT!
2 functions called "dispatch" at checked_result.hpp:119 and 
exception.hpp:33 need to be marked as constexpr in order for the binary 
operators to be constexpr, with that change then I can now write:
         constexpr const boost::numeric::safe<int> sy(42);
         ten_holder<static_cast<int>(sy)> holder3;   // OK sy is 
available at compile time
         constexpr const boost::numeric::safe<int> syy(sy*sy);
         ten_holder<static_cast<int>(syy)> holder4; // OK syy is 
available at compile time
:)
BTW my experience with both constexpr and noexcept is that it's simply 
not enough to test at runtime, you have to contrive compile time tests 
as well, otherwise I can absolutely guarantee things will not work as 
you expect - trust me I've been there, done that, got the t-shirt!
What that means in practice - and you're not going to like this - is 
that every runtime test has to have a compile time / constexpr 
counterpart, things that generate valid "safe" values should compile in 
a constexpr context (and yes the value needs checking to, again at 
compile time), while things that would throw at runtime, would be 
compile-fails.  It's a heck of a lot of tests - you might want to write 
a short program to spew out the files (the expected compile things can 
all go in one file, but expected failures each need their own test case 
cpp file).
HTH, John.
>
> constexpr const safe_int z(safe_signed_literal<42>());  // z is NOW 
> available at compile
>
> So the problem is that literals are not considered constexpr.  I 
> believe this is a problem is with the way constexpr is defined.
>
> Actually I have a whole rant on constexpr and const expressions. I 
> believe that C++ standard has made this a lot more complex and less 
> useful than it could be.  But I can pursue only one hopeless quest at 
> at time.
>
> Robert Ramey
>
>
>
> _______________________________________________
> Unsubscribe & other changes: 
> http://listarchives.boost.org/mailman/listinfo.cgi/boost
> .
>
--- This email has been checked for viruses by AVG. http://www.avg.com