Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling literal zero in unit-safe code

With a very few exceptions (looking at you, Fahrenheit and Celsius temperature scales), units are linear, and the value zero is simultaneously the additive identity for all units at once.

So given

auto speed = dimensioned(20, _meter/_second);
auto power = dimensioned(75, _watt);

then

if (speed < 6) ...
if (power > 17) ...

makes no more sense than

if (speed > power) ...

you should write

if (speed < dimensioned(6, _mile/_hour)) ...

However, this DOES make sense:

if (speed < 0)

because 0 m/s == 0 mph == 0 A.U./fortnight or any other units that you care to use (for velocity). The question then is how to enable this and only this usage.

C++11 explicit operators and contextual conversion to bool got rid of the need of the "safe-bool" idiom. It appears this problem can be solved with a comparable "safe-zero" idiom:

struct X
{
  int a;
  friend bool operator<(const X& left, const X& right) { return left.a < right.a; }
private:
  struct safe_zero_idiom;
public:
  friend bool operator<(const X& left, safe_zero_idiom*) { return left.a < 0; }
};
  • Demo: http://ideone.com/vUxmxi

Unfortunately it seems that deployed dimension/unit libraries aren't doing this. (This question arose because I actually wanted to test whether a std::chrono::duration was negative). Is this useful? Are there cases that would cause it to fail? Is there any easier way to permit comparison to zero?

One suspects that instead of implementing this for individual operators, there ought to exist an implicit conversion from literal zero to unit-tagged types.


I do note that it allows

 X{1} < nullptr

as a valid expression :(, and unfortunately providing an inaccessible overload of type std::nullptr_t doesn't fix this, since the Standard says in section 4.10

A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t.

like image 746
Ben Voigt Avatar asked Sep 25 '13 20:09

Ben Voigt


1 Answers

  1. Yes. You convinced me pretty easily that this is useful.
  2. You already pointed out a failure point, for nullptr. I couldn't think of anything other than that.
    My attempts at devising a mechanism to disallow nullptr but allow 0 all yielded complicated schemes that didn't work. Basically, since there is no way to tell C++ you want a constexpr function parameter, it is hard (I won't say impossible yet...) to devise a function that takes an int argument, but results in compile time failure if the argument value is not 0.
  3. If you are okay with allowing nullptr, then an easier implementation would be to use std::nullptr_t directly rather than a separate safe_zero_idiom class. (Admittedly, it is not as safe, since there is no way to access the safe_zero_idiom type in your implementation.)

struct X
{
  int a;
  friend bool operator<(const X& left, const X& right) { return left.a < right.a; }
  friend bool operator<(const X& left, std::nullptr_t) { return left.a < 0; }
};
like image 200
jxh Avatar answered Oct 18 '22 15:10

jxh