www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - how to use opdot

reply Morusaka <morusaka inwind.it> writes:
Hi,

I've read about opdot in D language spec operator overload section, but the
little snippet of code provided isn't enough, for me, to figure out what it is
supposed to do and how to use it or what it could be usefull for.

Could you please help me to get the right way?

Thank you,

Luca.
Nov 16 2008
next sibling parent reply Hoenir <mrmocool gmx.de> writes:
Morusaka schrieb:
 Hi,
 
 I've read about opdot in D language spec operator overload section, but the
little snippet of code provided isn't enough, for me, to figure out what it is
supposed to do and how to use it or what it could be usefull for.
 
 Could you please help me to get the right way?
 

e.g. the scripting language Lua does it that way: if it can't find a member in a table, it looks in another one specified similarly to this opDot way. That provides some kind of "inheritance". But I can't currently imagine any way to use this either.
Nov 19 2008
parent Kagamin <spam here.lot> writes:
Hoenir Wrote:

 But I can't currently imagine any way to use this either.

it will be useful when implementing auto_ptr, shared_ptr and likes.
Nov 20 2008
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Morusaka" wrote
 Hi,

 I've read about opdot in D language spec operator overload section, but 
 the little snippet of code provided isn't enough, for me, to figure out 
 what it is supposed to do and how to use it or what it could be usefull 
 for.

 Could you please help me to get the right way?

opDot is useful if you want to make a 'wrapper' type. That is, you want to mimic another type, but you want to slightly alter the behavior. opDot allows you to 'inherit' all the member functions and fields from the wrapped type. For example, if I wanted to create a wrapper type that added a 'blahblah' integer to the type, I could do this: struct AddBlahBlah(T) { T _t; int blahblah; T *opDot() { return &_t;} } Now, if I declare an AddBlahBlah!(C) and class C has a member foo(): C c; AddBlahBlah!(C) abb = AddBlahBlah!(C)(c); abb.foo(); // translates to abb.opDot().foo() abb.blahblah = 5; // sets abb.blahblah to 5, doesn't affect _t The goal of opDot is to allow one to create types that wrap other types that look almost exactly the same without much effort. For example, the std.typecons.Rebindable type allows one to create a rebindable const or invariant class reference while forwarding all member accesses to the underlying invariant or const instance. This feature is used for extending the type system without having to extend the language, allowing compiler enforcement of specific design aspects without defining them in the compiler. Normal developers will most likely never need to define opDot. -Steve
Nov 20 2008
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Steven Schveighoffer wrote:
 "Morusaka" wrote
 Hi,

 I've read about opdot in D language spec operator overload section, but 
 the little snippet of code provided isn't enough, for me, to figure out 
 what it is supposed to do and how to use it or what it could be usefull 
 for.

 Could you please help me to get the right way?

opDot is useful if you want to make a 'wrapper' type. That is, you want to mimic another type, but you want to slightly alter the behavior. opDot allows you to 'inherit' all the member functions and fields from the wrapped type. For example, if I wanted to create a wrapper type that added a 'blahblah' integer to the type, I could do this: struct AddBlahBlah(T) { T _t; int blahblah; T *opDot() { return &_t;} } Now, if I declare an AddBlahBlah!(C) and class C has a member foo(): C c; AddBlahBlah!(C) abb = AddBlahBlah!(C)(c); abb.foo(); // translates to abb.opDot().foo() abb.blahblah = 5; // sets abb.blahblah to 5, doesn't affect _t

Wow. That's incredibly useful for doing decorators! http://en.wikipedia.org/wiki/Decorator_pattern
Nov 20 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Ary Borenszweig" wrote
 Steven Schveighoffer wrote:
 "Morusaka" wrote
 Hi,

 I've read about opdot in D language spec operator overload section, but 
 the little snippet of code provided isn't enough, for me, to figure out 
 what it is supposed to do and how to use it or what it could be usefull 
 for.

 Could you please help me to get the right way?

opDot is useful if you want to make a 'wrapper' type. That is, you want to mimic another type, but you want to slightly alter the behavior. opDot allows you to 'inherit' all the member functions and fields from the wrapped type. For example, if I wanted to create a wrapper type that added a 'blahblah' integer to the type, I could do this: struct AddBlahBlah(T) { T _t; int blahblah; T *opDot() { return &_t;} } Now, if I declare an AddBlahBlah!(C) and class C has a member foo(): C c; AddBlahBlah!(C) abb = AddBlahBlah!(C)(c); abb.foo(); // translates to abb.opDot().foo() abb.blahblah = 5; // sets abb.blahblah to 5, doesn't affect _t

Wow. That's incredibly useful for doing decorators! http://en.wikipedia.org/wiki/Decorator_pattern

Not exactly ;) The wrapped type is not equivalent to inheritance. For example, if you have a function that takes a class C, you can't pass an AddBlahBlah!(C) type into it. However, a template function which expects a type C or a wrapped C, could possibly be used as you say. -Steve
Nov 20 2008
next sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Steven Schveighoffer wrote:
 "Ary Borenszweig" wrote
 Steven Schveighoffer wrote:
 "Morusaka" wrote
 Hi,

 I've read about opdot in D language spec operator overload section, but 
 the little snippet of code provided isn't enough, for me, to figure out 
 what it is supposed to do and how to use it or what it could be usefull 
 for.

 Could you please help me to get the right way?

to mimic another type, but you want to slightly alter the behavior. opDot allows you to 'inherit' all the member functions and fields from the wrapped type. For example, if I wanted to create a wrapper type that added a 'blahblah' integer to the type, I could do this: struct AddBlahBlah(T) { T _t; int blahblah; T *opDot() { return &_t;} } Now, if I declare an AddBlahBlah!(C) and class C has a member foo(): C c; AddBlahBlah!(C) abb = AddBlahBlah!(C)(c); abb.foo(); // translates to abb.opDot().foo() abb.blahblah = 5; // sets abb.blahblah to 5, doesn't affect _t

http://en.wikipedia.org/wiki/Decorator_pattern

Not exactly ;) The wrapped type is not equivalent to inheritance.

Ah, right. I forgot the inheritance part. :(
 
 For example, if you have a function that takes a class C, you can't pass an 
 AddBlahBlah!(C) type into it.
 
 However, a template function which expects a type C or a wrapped C, could 
 possibly be used as you say.
 
 -Steve 
 
 

Nov 20 2008
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Steven Schveighoffer wrote:
 Not exactly ;)  The wrapped type is not equivalent to inheritance.

No, exactly: class Wrapper(T) : T { T opDot() {} }
Nov 20 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Christopher Wright" wrote
 Steven Schveighoffer wrote:
 Not exactly ;)  The wrapped type is not equivalent to inheritance.

No, exactly: class Wrapper(T) : T { T opDot() {} }

class Wrapper(T) : T { } works just as good ;) -Steve
Nov 20 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Steven Schveighoffer wrote:
 "Christopher Wright" wrote
 Steven Schveighoffer wrote:
 Not exactly ;)  The wrapped type is not equivalent to inheritance.

class Wrapper(T) : T { T opDot() {} }

class Wrapper(T) : T { } works just as good ;) -Steve

True, but with opDot, you can swap out the real value. I can think of cases in which this would be useful -- a sort of "I'll fill in this value later" thing.
Nov 20 2008
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Christopher Wright escribió:
 Steven Schveighoffer wrote:
 "Christopher Wright" wrote
 Steven Schveighoffer wrote:
 Not exactly ;)  The wrapped type is not equivalent to inheritance.

class Wrapper(T) : T { T opDot() {} }

class Wrapper(T) : T { } works just as good ;) -Steve

True, but with opDot, you can swap out the real value. I can think of cases in which this would be useful -- a sort of "I'll fill in this value later" thing.

I'm not sure that's the decorator pattern any more. In that pattern you implement or extend a class, and receive an instance of one in the constructor and forward all calls to that instance. If you do: class Wrapper(T) : T { private wrapped; this(T wrapped) { this.wrapped = wrapped; } T opDot() { return wrapped; } } that won't work because whenever you call a method of T on Wrapper(T), that will call Wrapper's method, since it has it, because it extends/implements T (opDot won't be triggered). If you remove inheritance, that will probably work, but you won't be able to make a Wrapper(T) behave like a T for the type system.
Nov 21 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Ary Borenszweig" wrote
 Christopher Wright escribió:
 Steven Schveighoffer wrote:
 "Christopher Wright" wrote
 Steven Schveighoffer wrote:
 Not exactly ;)  The wrapped type is not equivalent to inheritance.

class Wrapper(T) : T { T opDot() {} }

class Wrapper(T) : T { } works just as good ;) -Steve

True, but with opDot, you can swap out the real value. I can think of cases in which this would be useful -- a sort of "I'll fill in this value later" thing.

I'm not sure that's the decorator pattern any more. In that pattern you implement or extend a class, and receive an instance of one in the constructor and forward all calls to that instance. If you do: class Wrapper(T) : T { private wrapped; this(T wrapped) { this.wrapped = wrapped; } T opDot() { return wrapped; } } that won't work because whenever you call a method of T on Wrapper(T), that will call Wrapper's method, since it has it, because it extends/implements T (opDot won't be triggered). If you remove inheritance, that will probably work, but you won't be able to make a Wrapper(T) behave like a T for the type system.

Exactly what I was going to say ;) If you inherit from a T, then return T in an opDot, opDot will never be called unless you call opDot directly. -Steve
Nov 21 2008
prev sibling parent Morusaka <morusaka inwind.it> writes:
Steven Schveighoffer Wrote:
 opDot is useful if you want to make a 'wrapper' type.  That is, you want to 
 mimic another type, but you want to slightly alter the behavior.  opDot 
 allows you to 'inherit' all the member functions and fields from the wrapped 
 type.  For example, if I wanted to create a wrapper type that added a 
 'blahblah' integer to the type, I could do this:
 
 struct AddBlahBlah(T)
 {
    T _t;
    int blahblah;
 
    T *opDot() { return &_t;}
 }
 
 Now, if I declare an AddBlahBlah!(C) and class C has a member foo():
 
 C c;
 AddBlahBlah!(C) abb = AddBlahBlah!(C)(c);
 
 abb.foo(); // translates to abb.opDot().foo()
 abb.blahblah = 5; // sets abb.blahblah to 5, doesn't affect _t
 
 The goal of opDot is to allow one to create types that wrap other types that 
 look almost exactly the same without much effort.  For example, the 
 std.typecons.Rebindable type allows one to create a rebindable const or 
 invariant class reference while forwarding all member accesses to the 
 underlying invariant or const instance.  This feature is used for extending 
 the type system without having to extend the language, allowing compiler 
 enforcement of specific design aspects without defining them in the 
 compiler.
 
 Normal developers will most likely never need to define opDot.
 
 -Steve 
 
 

Thank you very much Steven! Luca
Nov 20 2008