www.digitalmars.com         C & C++   DMDScript  

D - Delegates

reply "Walter" <walter digitalmars.com> writes:
A first crack at a proposal.
----------------------------------
A delegate is conceptually a function pointer coupled with an object
reference. If the function is a non-member or a static function, the object
reference is null.

There are three things to do with delegates 1) declaring them
2) initializing them and 3) calling them.

1. Declaring Delegates

A delegate needs a return type and a parameter list. Introduce the keyword
'delegate' and declare mydelegate as:

 int delegate(int x, int y) mydelegate;

An array of delegates would be:

 float delegate(int *p)[] foo; // foo is array of delegates

Delegates can be typedef'd:

 typedef int delegate(int x, int y) bar;
 bar x;   // x is a delegate

2. Initializing Delegates

Delegates are initialized with an object and a function signature.
They are initialized with a DelegateExpression:

 class Abc
 {
     int func(int,int);
 }

 Abc a = new Abc();
 int delegate(int, int) x;

 x = delegate(a, func(int,int));

The first argument to delegate(object, function signature) is the object
reference.

A delegate to a non-member or static function can be initialized
by using null as the object:

 int xyzzy(int, int);
 x = delegate(null, xyzzy(int, int));

3. Calling Delegates

 Delegates are called as if they are normal functions:

 x(3,4);  // call delegate x
Apr 02 2002
next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a8d501$2far$1 digitaldaemon.com...

 A first crack at a proposal.
 ----------------------------------
 A delegate is conceptually a function pointer coupled with an object
 reference. If the function is a non-member or a static function, the

 reference is null.

Seems like the best solution.
 There are three things to do with delegates 1) declaring them
 2) initializing them and 3) calling them.

 1. Declaring Delegates

 A delegate needs a return type and a parameter list. Introduce the keyword
 'delegate' and declare mydelegate as:

  int delegate(int x, int y) mydelegate;

Pretty fine. Looks more like variable declaration, and is much better than C function pointers;
 An array of delegates would be:

  float delegate(int *p)[] foo; // foo is array of delegates

Consistent.
 Delegates can be typedef'd:

  typedef int delegate(int x, int y) bar;
  bar x;   // x is a delegate

As well.
 2. Initializing Delegates

 Delegates are initialized with an object and a function signature.
 They are initialized with a DelegateExpression:

  class Abc
  {
      int func(int,int);
  }

  Abc a = new Abc();
  int delegate(int, int) x;

  x = delegate(a, func(int,int));

 The first argument to delegate(object, function signature) is the object
 reference.

Just a suggestion: if object is omitted, assume "this" where applicable: class Form { Button OK; void OK_Click(int x, int y) { ... } this() { OK.onClick = delegate(OK_Click(int, int)); } } Pointers to methods of "this" are 90% of all, anyhow.
 A delegate to a non-member or static function can be initialized
 by using null as the object:

  int xyzzy(int, int);
  x = delegate(null, xyzzy(int, int));

Why not just function pointers? x = &xyzzy(int, int); Or, allow to omit Object, assuming null when outside class.
 3. Calling Delegates

  Delegates are called as if they are normal functions:

  x(3,4);  // call delegate x

Yep.
Apr 02 2002
parent reply "J. Daniel Smith" <j_daniel_smith HoTMaiL.com> writes:
[ moved to this thread from the "new D site" thread ]

I would agree.  In C#, I loooks the "delegate" keyword actually causes the
compiler to do a "typedef" of "System.MulticastDelegate"; then it looks like
there is some additional checking to be sure that new "delegate" type is
only used with the "event" keyword to declare a variable:
   public delegate void Foo(int);    // typedef System.MulticastDelegate
Foo;
   public event Foo ptr;              // Foo ptr;
Of course, things are a bit more complicated than that since all the
type-checking has to be done.  In C#, the argument list is an array of
Objects.

Microsoft has released the source code for the SDK version of .NET; see
http://msdn.microsoft.com/library/en-us/Dndotnet/html/mssharsourcecli.asp.
Looking through the code in "delegate.cs" is interesting.

   Dan

[ edited delegate.cs ]




"Pavel Minayev" <evilone omen.ru> wrote in message
news:a8evkh$5tf$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:a8eie6$40d$1 digitaldaemon.com...

 The C# syntax I find very confusing. The delegate declaration looks like


 storage class, but it isn't, it's a type component. It doesn't seem

 in C#, because of the way declarations work, to create an array of

 or a pointer to a delegate or a function returning a delegate.

It wasn't actually C# syntax. In C#, "delegate" is actually a keyword which declares type, and not variable: delegate void Foo(int); // Foo is a new type Foo ptr; // declare a pointer of that type So, you can actually have arrays: Foo[] ptr; Still, I like your syntax more. It is more distinct from normal function declaration.
 The static/virtual delegate call shouldn't cause a slowdown in D, for


 happy coincidence that the 'this' pointer is passed in EAX. Hence, just
 stuff the object reference in EAX after pushing the parameters. If the
 function doesn't take a this pointer, it ignores EAX with no adverse
 consequences.

It's implementation detail, I guess. As long as it works, great, and no matter how it is done. I wouldn't mind if it were slower than normal ptr call, but if you can make it the same, well, it's great! By the way, I guess functions with non-D calling conventions cannot be delegated?

Apr 03 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
Um - The paranoid part of me thinks, Walter, that you should delete that
post, since it contains Microsoft copyrighted code.  I say this without
knowing if it's actually illegal to have posted a copy, but rather from
extreme caution.  MS might come in later and say that, having read this
source you have agreed to the source license, and the source license states
that everything you gain from reading the source belongs to MS.

Anyway, if you want to learn about C# by looking at source code, there's the
Mono project (www.go-mono.com) which is "libre" open source.

"J. Daniel Smith" <j_daniel_smith HoTMaiL.com> wrote in message
news:a8f65e$bal$1 digitaldaemon.com...
 Microsoft has released the source code for the SDK version of .NET; see
 http://msdn.microsoft.com/library/en-us/Dndotnet/html/mssharsourcecli.asp.
 Looking through the code in "delegate.cs" is interesting.

    Dan

Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Apr 03 2002
parent "J. Daniel Smith" <j_daniel_smith HoTMaiL.com> writes:
I think the provision "You may use any information in intangible form that
you remember after accessing the Software." covers the D effort with regards
to this.  I removed most of the code that actually does anything from my
post since that didn't seem very relevent to the discussion; thus the code I
posted won't compile, and even it it does, it won't do anything useful.

Unless the D compiler morphs into something that can compile C# code, the
code snipet would seem to fall under the "academic research" category for
use of "the Software".

   Dan

"Richard Krehbiel" <rich kastle.com> wrote in message
news:a8fg3e$289$1 digitaldaemon.com...
 Um - The paranoid part of me thinks, Walter, that you should delete that
 post, since it contains Microsoft copyrighted code.  I say this without
 knowing if it's actually illegal to have posted a copy, but rather from
 extreme caution.  MS might come in later and say that, having read this
 source you have agreed to the source license, and the source license

 that everything you gain from reading the source belongs to MS.

 Anyway, if you want to learn about C# by looking at source code, there's

 Mono project (www.go-mono.com) which is "libre" open source.

 "J. Daniel Smith" <j_daniel_smith HoTMaiL.com> wrote in message
 news:a8f65e$bal$1 digitaldaemon.com...
 Microsoft has released the source code for the SDK version of .NET; see


 Looking through the code in "delegate.cs" is interesting.

    Dan

 [ edited delegate.cs ]
 // ==++==
 //
 //
 //    Copyright (c) 2002 Microsoft Corporation.  All rights reserved.
 //
 //    The use and distribution terms for this software are contained in

 file
 //    named license.txt, which can be found in the root of this
 distribution.
 //    By using this software in any fashion, you are agreeing to be


 by
 the
 //    terms of this license.
 //
 //    You must not remove this notice, or any other, from this software.

-- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)

Apr 03 2002
prev sibling next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Looks really good!

--
The Villagers are Online! villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Apr 02 2002
prev sibling next sibling parent reply "OddesE" <OddesE_XYZ hotmail.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a8d501$2far$1 digitaldaemon.com...
 A first crack at a proposal.
 ----------------------------------
 A delegate is conceptually a function pointer coupled with an object
 reference. If the function is a non-member or a static function, the

 reference is null.

 There are three things to do with delegates 1) declaring them
 2) initializing them and 3) calling them.

 1. Declaring Delegates

 A delegate needs a return type and a parameter list. Introduce the keyword
 'delegate' and declare mydelegate as:

  int delegate(int x, int y) mydelegate;

 An array of delegates would be:

  float delegate(int *p)[] foo; // foo is array of delegates

 Delegates can be typedef'd:

  typedef int delegate(int x, int y) bar;
  bar x;   // x is a delegate

 2. Initializing Delegates

 Delegates are initialized with an object and a function signature.
 They are initialized with a DelegateExpression:

  class Abc
  {
      int func(int,int);
  }

  Abc a = new Abc();
  int delegate(int, int) x;

  x = delegate(a, func(int,int));

 The first argument to delegate(object, function signature) is the object
 reference.

 A delegate to a non-member or static function can be initialized
 by using null as the object:

  int xyzzy(int, int);
  x = delegate(null, xyzzy(int, int));

 3. Calling Delegates

  Delegates are called as if they are normal functions:

  x(3,4);  // call delegate x

Oh oh, I sense another rewrite of Pavel's WinD module coming up... :) But I guess you won't mind at all eh Pavel, this is pretty much what you want isn't it? -- Stijn OddesE_XYZ hotmail.com http://OddesE.cjb.net _________________________________________________ Remove _XYZ from my address when replying by mail
Apr 02 2002
parent "Pavel Minayev" <evilone omen.ru> writes:
"OddesE" <OddesE_XYZ hotmail.com> wrote in message
news:a8ddi3$ret$1 digitaldaemon.com...

 Oh oh, I sense another rewrite of Pavel's
 WinD module coming up... :)
 But I guess you won't mind at all eh Pavel,
 this is pretty much what you want isn't it?

Well, yes =)
Apr 02 2002
prev sibling next sibling parent "J. Daniel Smith" <j_daniel_smith HoTMaiL.com> writes:
The way C# goes about implementing delegates is interesting: the compiler
*requires* that certain classes (in this case System.Delegate and friends)
exist at run-time.  This seems to harken back to languages like Pascal where
writeln() was part of the the language, but it was implemented in a run-time
library; it is a different approach from C/C++ (and D) where printf() is
"only" a library routine.

Might the C# way of doing things have some value in D?  For this new
"delegate" feature, not only does the compiler have to support the new
syntax, but then it also has to generate a bunch of code to make the syntax
work.  By moving the actual implementing into a class, there is much less
work for the compiler to do: when it sees the "delegate" keyword is knows to
use the System.Delegate class in a certain manner.

Of course, this approach opens might open a whole can of worms with regards
to issues that have been hashed over to some length already such as operator
overloading, since you now have to be able to write a "delegate", "string",
or "complex" class in D itself - the compiler can't do as much "black magic"
to make such things work.

Putting the bulk of the "real work" in a class would also seem to make it
easier to port D to other platforms since the compiler itself would now be
simplier.

   Dan

"Walter" <walter digitalmars.com> wrote in message
news:a8d501$2far$1 digitaldaemon.com...
 A first crack at a proposal.
 ----------------------------------
 A delegate is conceptually a function pointer coupled with an object
 reference. If the function is a non-member or a static function, the

 reference is null.

 There are three things to do with delegates 1) declaring them
 2) initializing them and 3) calling them.
 [...]

Apr 04 2002
prev sibling parent reply roland <nancyetroland free.fr> writes:
Walter a écrit :

 A first crack at a proposal.

beautiful !
  x = delegate(a, func(int,int));

i don't know why, but i feel delegate initialisation can be nicer: x = delegate(a.func(int,int)); //? or if "a" is *this: x = delegate(func(int,int)) //? or even if there is only one methode called func: x = delegate(a.func); //? roland
Apr 04 2002
parent reply Roland <rv ronetech.com> writes:
roland a écrit :

 i don't know why, but i feel delegate initialisation  can be nicer:

.
 or even if there is only one methode called func:

rectification: as function args are declared with delegate, there is no ambiguity about witch methode is to be delegated. so, delegate initialisation could, in theory, always be this format.
 x = delegate(a.func);            //?

roland
Apr 04 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Roland" <rv ronetech.com> wrote in message
news:3CAD5197.5CC2DA84 ronetech.com...
 rectification: as function args are declared with delegate, there is no
 ambiguity about witch methode is to be delegated.
 so, delegate initialisation could, in theory, always be this format.

 x = delegate(a.func);            //?


Type information (typically) does not flow left-to-right across assignment expressions; in other words, the "delegate" operation can't peek at the "x" across the "=" to find anything out. But actually, having said that, I already know there's an exception in D. String literals become char literals and wide strings based on their assignment context, so maybe delegates can do similar magic. -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Apr 05 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Richard Krehbiel" <rich kastle.com> wrote in message
news:a8k6de$5na$1 digitaldaemon.com...
 But actually, having said that, I already know there's an exception in D.
 String literals become char literals and wide strings based on their
 assignment context, so maybe delegates can do similar magic.

I had the same thought, but I still haven't figured out what to do in ambiguous cases like: void foo(char[]); void foo(wchar[]); ... foo("hello"); Which gets called? Should it be an error? In any case, I'm leaning towards initializing a delegate with the syntax: x = &o.func; and then using the context to figure out which overloaded func it should be. This does add some complexity to the compiler I don't like.
Apr 05 2002
next sibling parent reply Russell Borogove <kaleja estarcion.com> writes:
Walter wrote:
 I had the same thought, but I still haven't figured out what to do in
 ambiguous cases like:
 
     void foo(char[]);
     void foo(wchar[]);
     ...
     foo("hello");
 
 Which gets called? Should it be an error?

Hey, I'm not following the discussion of delegates too closely, but I've got an opinion on this one. I'd want a warning on that so as to avoid consequences of calling the wrong one; since you don't believe in warnings, it should be an error. -RB
Apr 05 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3CADE8E0.1070307 estarcion.com...

 I'd want a warning on that so as to avoid consequences
 of calling the wrong one; since you don't believe in
 warnings, it should be an error.

Imagine: class Stream { void write(char[] s) { ... } void write(wchar[] s) { ... } } That's right, two versions, because you might want to feed char[] or wchar[] variable to it. Now, you write: stdout.write("Hello, world!"); And get the compiler error, 'cause you must do: stdout.write(cast(char[]) "Hello, world!"); You like it?
Apr 05 2002
parent reply Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:
 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3CADE8E0.1070307 estarcion.com...
 
 
I'd want a warning on that so as to avoid consequences
of calling the wrong one; since you don't believe in
warnings, it should be an error.

Imagine: class Stream { void write(char[] s) { ... } void write(wchar[] s) { ... } } That's right, two versions, because you might want to feed char[] or wchar[] variable to it. Now, you write: stdout.write("Hello, world!"); And get the compiler error, 'cause you must do: stdout.write(cast(char[]) "Hello, world!"); You like it?

This language design thing is hard. :) Like you, I'd have to lean towards differentiating char and wchar literals, or default to char unless there's a \uXXXX in there somewhere. The latter might give rise to the (ugly) idiom: "Hello world!\u0000" to force wide char literal strings. Sidethread, you complain about the L"literal" syntax as ugly, but it's far less ugly than the cast. -R
Apr 05 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3CADFE2F.4070502 estarcion.com...

 This language design thing is hard. :) Like you, I'd
 have to lean towards differentiating char and wchar
 literals, or default to char unless there's a \uXXXX
 in there somewhere. The latter might give rise to the
 (ugly) idiom: "Hello world!\u0000" to force wide char
 literal strings.

It isn't proper way since D strings are not 0-terminated, so if you print it, the null char will be printed as well, I guess. But still the idea is interesting. The compiler should be able to determine whether string is char or wchar on his own. Also, if you have function overloaded for both, then it's not very likely that you have to force wchar literals, since char version would do exactly the same (e.g. print the string).
 Sidethread, you complain about the L"literal" syntax
 as ugly, but it's far less ugly than the cast.

Yep, right. But still, L is hard to parse and to distinguish. I think that suffix would be better, "w" or something: "Hello, world!"W
Apr 05 2002
parent reply Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:
 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3CADFE2F.4070502 estarcion.com...
 
 
This language design thing is hard. :) Like you, I'd
have to lean towards differentiating char and wchar
literals, or default to char unless there's a \uXXXX
in there somewhere. The latter might give rise to the
(ugly) idiom: "Hello world!\u0000" to force wide char
literal strings.

It isn't proper way since D strings are not 0-terminated, so if you print it, the null char will be printed as well, I guess.

Ohhh, crap. Well, I lobbied for padding (at least char and wchar) arrays with an extra zero-valued element to make conversion to C-strings easier way back when, but noooooooo....
 But still the idea is interesting. The compiler should
 be able to determine whether string is char or wchar
 on his own. Also, if you have function overloaded for both,
 then it's not very likely that you have to force wchar
 literals, since char version would do exactly the same
 (e.g. print the string).

I can contrive cases where the char and wchar versions do sufficiently different things (e.g. render from a 100KB TrueType font versus render from a 20MB TrueType font) that the impact would be significant, but even such cases aren't catastrophic. As you point out, the numeric promotions and conversions are probably more troublesome. -RB
Apr 05 2002
next sibling parent "Walter" <walter digitalmars.com> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3CAE8210.4020908 estarcion.com...
 Ohhh, crap. Well, I lobbied for padding (at least char
 and wchar) arrays with an extra zero-valued element to
 make conversion to C-strings easier way back when, but
 noooooooo....

Actually, D does 0 terminate them when it can, for just that reason. But if you slice an array out of another char[], it won't be 0 terminated.
Apr 05 2002
prev sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3CAE8210.4020908 estarcion.com...

 Ohhh, crap. Well, I lobbied for padding (at least char
 and wchar) arrays with an extra zero-valued element to
 make conversion to C-strings easier way back when, but
 noooooooo....

Literals are terminated with 0 in memory. However, 0 is not considered element of the array: "foo" -> 'f', 'o', 'o', 0 "foo".length == 3;
Apr 06 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a8mk4r$n0q$1 digitaldaemon.com...
 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3CAE8210.4020908 estarcion.com...

 Ohhh, crap. Well, I lobbied for padding (at least char
 and wchar) arrays with an extra zero-valued element to
 make conversion to C-strings easier way back when, but
 noooooooo....

Literals are terminated with 0 in memory. However, 0 is not considered element of the array: "foo" -> 'f', 'o', 'o', 0 "foo".length == 3;

That's right. It's a bit of a hack, but a hack that works for the most common cases, like passing a string literal to printf!
Apr 06 2002
parent "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a8nalt$1fsi$1 digitaldaemon.com...

 Literals are terminated with 0 in memory. However, 0 is not considered
 element of the array:

     "foo" -> 'f', 'o', 'o', 0
     "foo".length == 3;

That's right. It's a bit of a hack, but a hack that works for the most common cases, like passing a string literal to printf!

It's also great when interfacing with APIs and such. Well, it just works, and variables can always be converted using toStringz().
Apr 06 2002
prev sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a8kn36$2hic$1 digitaldaemon.com...

 I had the same thought, but I still haven't figured out what to do in
 ambiguous cases like:

     void foo(char[]);
     void foo(wchar[]);
     ...
     foo("hello");

 Which gets called? Should it be an error?

I guess it shouldn't. The reason is, you will probably define a wchar version for each string-handling function, and if it was considered an error, you wouldn't be able to call those functions with literals as arguments - which is convenient sometimes. I'd personally suggest to make some way to differentiate between char and wchar literals (but not that ugly L"..."!).
 In any case, I'm leaning towards initializing a delegate with the syntax:

     x = &o.func;

 and then using the context to figure out which overloaded func it should

 This does add some complexity to the compiler I don't like.

Well if function is NOT overloaded (which will most likely be the case), what's the sense of specifying parameters explicitly? Also, even for overloaded functions, I think the & syntax is better: x = &o.func(int, int);
Apr 05 2002