
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://lists.boost.org/mailman/listinfo.cgi/boost .
--- This email has been checked for viruses by AVG. http://www.avg.com