www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - opUnary overloading

reply "cal" <callumenator gmail.com> writes:
I have a struct wrapping a union of an int and a float, and want 
to overload opUnary for this struct:

struct Value
{
     enum Type {
         INT, FLOAT
     }

     Type type;

     union {
         int i;
         float f;
     }

     this(int _i) {
         i = _i;
         type = Type.INT;
     }

     this(float _f) {
         f = _f;
         type = Type.FLOAT;
     }

     auto opUnary(string s)() if (s == "-") {
         if (type == Type.INT)
             return -i;
         if (type == Type.FLOAT) // If i comment this out, I get 
an int back
             return -f;          // as expected
         assert(0);
     }
}

Value v1 = Value(25);
assert(v1.type == Value.Type.INT);
auto neg = -v1;
writeln(typeof(neg).stringof); // this returns float, when it 
should return int

The part I don't understand is this: in the opUnary method, if I 
comment out the second if conditional (if (type == Type.FLOAT)) 
then when doing the negation on v1 I get an INT as expected. If I 
don't comment this code out, I get a float.

When commented out, I get back -25 as an int, which i expect. 
When not commented out, I get back -nan, which suggests to me 
that it is returning the (uninitialized) float from the union, 
not simply giving me back the int as a float.

Could someone explain this?
Aug 26 2012
next sibling parent "cal" <callumenator gmail.com> writes:
I should clarify that I realize the auto cannot be used at 
runtime to select the return type - I just don't understand why 
the float part of the union is returned at runtime, instead of 
the int converted to whatever the compiler determines the 
return-type of opUnary to be.
Aug 26 2012
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/26/2012 10:33 PM, cal wrote:
 I have a struct wrapping a union of an int and a float, and want to
 overload opUnary for this struct:

 struct Value
 {
      enum Type {
          INT, FLOAT
      }

      Type type;

      union {
          int i;
          float f;
      }

      this(int _i) {
          i = _i;
          type = Type.INT;
      }

      this(float _f) {
          f = _f;
          type = Type.FLOAT;
      }

      auto opUnary(string s)() if (s == "-") {
          if (type == Type.INT)
              return -i;
          if (type == Type.FLOAT) // If i comment this out, I get an int
 back
              return -f;          // as expected
          assert(0);
      }
 }

 Value v1 = Value(25);
 assert(v1.type == Value.Type.INT);
 auto neg = -v1;
 writeln(typeof(neg).stringof); // this returns float, when it should
 return int

 The part I don't understand is this: in the opUnary method, if I comment
 out the second if conditional (if (type == Type.FLOAT)) then when doing
 the negation on v1 I get an INT as expected. If I don't comment this
 code out, I get a float.

 When commented out, I get back -25 as an int, which i expect. When not
 commented out, I get back -nan, which suggests to me that it is
 returning the (uninitialized) float from the union, not simply giving me
 back the int as a float.

 Could someone explain this?

Compiler bug. http://d.puremagic.com/issues/ Also see: http://d.puremagic.com/issues/show_bug.cgi?id=8307 I assume the compiler fails to add an implicit conversion node into the AST because at the point where -i is returned, the return type is still assumed to be 'int'. What you are observing is not type coercion, but an x87 FPU stack underflow.
Aug 26 2012
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/26/2012 01:33 PM, cal wrote:
 I have a struct wrapping a union of an int and a float, and want to
 overload opUnary for this struct:

 struct Value
 {
 enum Type {
 INT, FLOAT
 }

 Type type;

 union {
 int i;
 float f;
 }

 this(int _i) {
 i = _i;
 type = Type.INT;
 }

 this(float _f) {
 f = _f;
 type = Type.FLOAT;
 }

 auto opUnary(string s)() if (s == "-") {
 if (type == Type.INT)
 return -i;
 if (type == Type.FLOAT) // If i comment this out, I get an int back
 return -f; // as expected
 assert(0);
 }
 }

 Value v1 = Value(25);
 assert(v1.type == Value.Type.INT);
 auto neg = -v1;
 writeln(typeof(neg).stringof); // this returns float, when it should
 return int

 The part I don't understand is this: in the opUnary method, if I comment
 out the second if conditional (if (type == Type.FLOAT)) then when doing
 the negation on v1 I get an INT as expected. If I don't comment this
 code out, I get a float.

 When commented out, I get back -25 as an int, which i expect. When not
 commented out, I get back -nan, which suggests to me that it is
 returning the (uninitialized) float from the union, not simply giving me
 back the int as a float.

 Could someone explain this?

opUnary should return Value: Value opUnary(string s)() if (s == "-") { final switch (type) with (Type) { case INT: return Value(-i); case FLOAT: return Value(-f); } } I have also taken advantage of - 'final switch' to obviate the assert(0) - 'with' to simplify syntax (no need to be explicit as in 'Type.INT' anymore) Ali -- D Programming Language Tutorial: http://ddili.org/ders/d.en/index.html
Aug 26 2012
prev sibling parent "cal" <callumenator gmail.com> writes:
On Sunday, 26 August 2012 at 20:54:35 UTC, Timon Gehr wrote:
 Also see:
 http://d.puremagic.com/issues/show_bug.cgi?id=8307

 I assume the compiler fails to add an implicit conversion node 
 into the
 AST because at the point where -i is returned, the return type 
 is still
 assumed to be 'int'. What you are observing is not type 
 coercion, but
 an x87 FPU stack underflow.

"If there are multiple ReturnStatements, the types of them must match exactly." Understood, I wasn't doing this.
Aug 26 2012