www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opAssign?

reply "Garett Bass" <gtbass studiotekne.com> writes:
I'd like to have an opAssign() operator.  This would allow 
me to create new numeric types with syntax similar to 
built-in numeric types.  For example:

    struct clampf {
        private float value;
        opAssign(float assign) {
            assign = (assign =< 0) ? 0 : assign;
            value  = (assign >= 1) ? 1 : assign;
        }
        opAssign(clampf assign) {
            opAssign(assign.value);
        }
        // additional operators, eg. opAdd() etc.
    }

    clampf r, g, b;
    r = 0.5; // r.value == 0.5;
    g = r;   // g.value == 0.5;
    b = 2.f; // b.value == 1.0;

Then I could avoid clamping floating point values all the 
time, because I would always know that clampf's are clamped, 
and I could use the normal built-in type syntax.  It would 
also be handy to have implicit return functions, eg.:

    struct clampf {
        // ... see above
        float retFloat() {
            return value;
        }
    }

To allow numeric use such as:

    float f;
    f = r; // f == 0.5 (ie. f = r.retFloat())

Further, if this were all template friendly, then I could 
easily make a variety of clamp types with different limits. 
Thoughts, comments?

Regards,
Garett

 
Dec 17 2004
parent reply David Medlock <amedlock nospam.org> writes:
This may be feasible for structs, but not for classes I think.

class A has member function:

A opAssign( A other ) {... ; return other;}


Now I type
A foo = new A();
A bar = new A();

foo = bar;

what happens?  Is foo now un-rebindable?

Garett Bass wrote:
 I'd like to have an opAssign() operator.  This would allow 
 me to create new numeric types with syntax similar to 
 built-in numeric types.  For example:
 
     struct clampf {
         private float value;
         opAssign(float assign) {
             assign = (assign =< 0) ? 0 : assign;
             value  = (assign >= 1) ? 1 : assign;
         }
         opAssign(clampf assign) {
             opAssign(assign.value);
         }
         // additional operators, eg. opAdd() etc.
     }
 
     clampf r, g, b;
     r = 0.5; // r.value == 0.5;
     g = r;   // g.value == 0.5;
     b = 2.f; // b.value == 1.0;
 
 Then I could avoid clamping floating point values all the 
 time, because I would always know that clampf's are clamped, 
 and I could use the normal built-in type syntax.  It would 
 also be handy to have implicit return functions, eg.:
 
     struct clampf {
         // ... see above
         float retFloat() {
             return value;
         }
     }
 
 To allow numeric use such as:
 
     float f;
     f = r; // f == 0.5 (ie. f = r.retFloat())
 
 Further, if this were all template friendly, then I could 
 easily make a variety of clamp types with different limits. 
 Thoughts, comments?
 
 Regards,
 Garett
 
  
 
 

Dec 17 2004
next sibling parent reply "Garett Bass" <gtbass studiotekne.com> writes:
David,

    I see your point.  However, I still see the value of 
opAssign().  Let the usual reference assignment take place 
when there is no opAssign() defined, and let opAssign() do 
its intended business if it is defined.  Thus, defining 
opAssign() means "Objects of this type don't want to be 
rebound, they want to copy-on-assignment".

    What makes things really strange is if you allow static 
opAssign(), which would effectively allow foo to copy 
whatever static values were present in new A().  Does that 
make sense at all?  I suspect static opAssign() would be an 
error.  Are static operator functions always considered 
erroneous?

    I think the numeric syntax advantages make this 
appealing.  I always wondered why there is unsigned int, but 
no unsigned float.  Certainly a 0..1, or -1..1 number class 
can be very handy at times, and it sure would be nice to 
keep the bounds checking centralized without sacrificing 
normal numeric syntax.

Regards,
Garett


"David Medlock" <amedlock nospam.org> wrote in message 
news:cq0aa0$krt$1 digitaldaemon.com...
 This may be feasible for structs, but not for classes I 
 think.

 class A has member function:

 A opAssign( A other ) {... ; return other;}


 Now I type
 A foo = new A();
 A bar = new A();

 foo = bar;

 what happens?  Is foo now un-rebindable?

Dec 17 2004
next sibling parent reply David Medlock <amedlock nospam.org> writes:
I clearly see the advantage of it, don't get me wrong.  I just like the
simple semantics of D and wouldn't want it to get too messy.

For things such as ranges(as you stated ) as well as Metrix/English 
measurement classes with auto conversions would be very nice.

As for static OpXXX() functions, I think they are ignored when you use 
the operators(since you don't have a this variable they wouldnt be very 
useful anyway).

Cheers,
-Ash

Garett Bass wrote:
 David,
 
     I see your point.  However, I still see the value of 
 opAssign().  Let the usual reference assignment take place 
 when there is no opAssign() defined, and let opAssign() do 
 its intended business if it is defined.  Thus, defining 
 opAssign() means "Objects of this type don't want to be 
 rebound, they want to copy-on-assignment".
 
     What makes things really strange is if you allow static 
 opAssign(), which would effectively allow foo to copy 
 whatever static values were present in new A().  Does that 
 make sense at all?  I suspect static opAssign() would be an 
 error.  Are static operator functions always considered 
 erroneous?
 
     I think the numeric syntax advantages make this 
 appealing.  I always wondered why there is unsigned int, but 
 no unsigned float.  Certainly a 0..1, or -1..1 number class 
 can be very handy at times, and it sure would be nice to 
 keep the bounds checking centralized without sacrificing 
 normal numeric syntax.
 
 Regards,
 Garett
 
 
 "David Medlock" <amedlock nospam.org> wrote in message 
 news:cq0aa0$krt$1 digitaldaemon.com...
 
This may be feasible for structs, but not for classes I 
think.

class A has member function:

A opAssign( A other ) {... ; return other;}


Now I type
A foo = new A();
A bar = new A();

foo = bar;

what happens?  Is foo now un-rebindable?


Dec 18 2004
parent "Garett Bass" <gtbass studiotekne.com> writes:
David,

    Thanks for your points.  I've decided to use opCast() 
and opCall() to acheive the desired effect.  I have to 
cast(float) to assign a clampf to a float, and a(0.5) to 
assign a float to a clampf, but that hardly seems 
unreasonable, and it makes certain the user knows that 
clampf is not just a float.  I'm happy with the result.

Regards,
Garett 
Dec 18 2004
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Garett Bass wrote:
 David,
 
     I see your point.  However, I still see the value of 
 opAssign().  Let the usual reference assignment take place 
 when there is no opAssign() defined, and let opAssign() do 
 its intended business if it is defined.  Thus, defining 
 opAssign() means "Objects of this type don't want to be 
 rebound, they want to copy-on-assignment".

If you want C++, you (I guess) know where to find it. Seriously, I'm not sure that overriding = to copy class objects would be useful or desirable. A virtue of the current = operator is that it is consistently defined for everything (copy whatever its right operand represents, be it a value or a reference), and this is useful for generic programming. If its semantics are changed for certain types, someone will try to use templates that expect the default behaviour, possibly leading to bugs that are hard to diagnose. See also, if you haven't already http://www.digitalmars.com/d/faq.html#assignmentoverloading Of course, if you want to be able to make a copy of an object, you can always define a .dup property.
     What makes things really strange is if you allow static 
 opAssign(), which would effectively allow foo to copy 
 whatever static values were present in new A().  Does that 
 make sense at all?  I suspect static opAssign() would be an 
 error.  Are static operator functions always considered 
 erroneous?

No. They denote the operator applied to the struct/union/class name. So if we had opAssign, it would lead to class Qwert { static Qwert opAssign(int i) { ... } } void main() { Qwert = 42; } if that makes sense. For a more meaningful example (using opCall), see D/25334
     I think the numeric syntax advantages make this 
 appealing.  I always wondered why there is unsigned int, but 
 no unsigned float.  Certainly a 0..1, or -1..1 number class 
 can be very handy at times, and it sure would be nice to 
 keep the bounds checking centralized without sacrificing 
 normal numeric syntax.

Hmm ... maybe. Maybe someone else can provide a more detailed opinion.... Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on on the 'group where everyone may benefit.
Dec 21 2004
prev sibling parent Benjamin Herr <ben 0x539.de> writes:
David Medlock wrote:
 This may be feasible for structs, but not for classes I think.
 
 class A has member function:
 
 A opAssign( A other ) {... ; return other;}
 
 
 Now I type
 A foo = new A();
 A bar = new A();
 
 foo = bar;
 
 what happens?  Is foo now un-rebindable?

This is rather an issue with the ambiguity of `=', being binding operator for object references and assignment operator for everything else (and equality operator in Pascal, eeks). Perhaps we need to change the syntax for variable binding in a later iteration of D, to enable opAssign for class instances? foo := bar?? Another syntax for assignment is much more evil as it would need to apply to all other types to be really consistent? Oh, I know nothing of this is going to happen to D, but let me dream :( I will just write foo.opAssign(bar) in the meantime! -ben
Dec 18 2004