digitalmars.D.learn - Can't understand the application of delegates in toString() functions
- Heisenberg (16/51) Nov 07 2016 Hi there. I'm currently following Ali Çehreli's "Programming in
- Adam D. Ruppe (5/7) Nov 07 2016 You pass the string to the delegate, which does whatever with it
- Heisenberg (4/12) Nov 07 2016 It returns nothing, and just takes an array of characters as a
- =?UTF-8?Q?Ali_=c3=87ehreli?= (17/31) Nov 07 2016 Exactly how it happens requires explaining a long chain of function
- Heisenberg (4/23) Nov 07 2016 I see now. :) Sounds like I have to finish the whole book to be
- Mathias Lang (40/54) Nov 07 2016 When you ask for a string (or more generally, an array), you put
- Heisenberg (4/12) Nov 07 2016 I see.. So we're basically adding stuff to it and then printing
Hi there. I'm currently following Ali Çehreli's "Programming in D" book and I can't seem to be able to wrap my head around the of delegates in the toString() functions.. http://ddili.org/ders/d.en/lambda.html (Bottom of the page)We have defined many toString() functions up to this point in the book to represent objects as strings. Those toString() definitions all returned a string without taking any parameters. As noted by the comment lines below, structs and classes took advantage of toString() functions of their respective members by simply passing those members to format():... code ...In order for polygon to be sent to the output as a string on the last line of the program, all of the toString() functions of Polygon, ColoredPoint, Color, and Point are called indirectly, creating a total of 10 strings in the process. Note that the strings that are constructed and returned by the lower-level functions are used only once by the respective higher-level function that called them.Okay, I get it.However, although a total of 10 strings get constructed, only the very last one is printed to the output: [{RGB:10,10,10;(1,1)}, {RGB:20,20,20;(2,2)}, {RGB:30,30,30;(3,3)}] However practical, this method may degrade the performance of the program because of the many string objects that are constructed and promptly thrown away.I can see the reasoning behind this. But ..However practical, this method may degrade the performance of the program because of the many string objects that are constructed and promptly thrown away. An overload of toString() avoids this performance issue by taking a delegate parameter: CODE: void toString(void delegate(const(char)[]) sink) const; As seen in its declaration, this overload of toString() does not return a string. Instead, the characters that are going to be printed are passed to its delegate parameter. It is the responsibility of the delegate to append those characters to the single string that is going to be printed to the output. All the programmer needs to do differently is to call std.format.formattedWrite instead of std.string.format and pass the delegate parameter as its first parameter:Why? How can a delegate which returns nothing be used as an array which is going to be printed on the screen? And just how is it printed? The Documentation didn't make it much clearer.. the 'sink' is supposed to be the 'writer', isn't it? So what does it do? The arguments get interpreted and formatted into the string, but what happens after that? P.S. I've just checked the book, it doesn't seem to be explained anywhere properly (the way formattedRead is) P.P.S. I'm following the PDF version of the book
Nov 07 2016
On Monday, 7 November 2016 at 16:22:17 UTC, Heisenberg wrote:Why? How can a delegate which returns nothing be used as an array which is going to be printed on the screen?You pass the string to the delegate, which does whatever with it somewhere else. So you call: `passed_delegate("your string");` and it can forward it to writeln or whatever.
Nov 07 2016
On Monday, 7 November 2016 at 16:33:30 UTC, Adam D. Ruppe wrote:On Monday, 7 November 2016 at 16:22:17 UTC, Heisenberg wrote:But how does it forward it if it's:Why? How can a delegate which returns nothing be used as an array which is going to be printed on the screen?You pass the string to the delegate, which does whatever with it somewhere else. So you call: `passed_delegate("your string");` and it can forward it to writeln or whatever.void delegate(const(char)[]) sink // ?It returns nothing, and just takes an array of characters as a parameter.. Just how does it print?..
Nov 07 2016
On 11/07/2016 08:40 AM, Heisenberg wrote:On Monday, 7 November 2016 at 16:33:30 UTC, Adam D. Ruppe wrote:Exactly how it happens requires explaining a long chain of function calls. Probably that's why the author did not elaborate further. ;) I've probably omitted some steps here but I think the following is close enough: - writeln() that's in the sample code calls write() with the addition of '\n'. - write() eventually calls formattedWrite() with a lockingTextWriter as the writer: https://github.com/dlang/phobos/blob/master/std/stdio.d#L1401 - formattedWrite() calls formatValue(): https://github.com/dlang/phobos/blob/master/std/format.d#L2906 - formatValue() calls formatObject() and that's the one that makes a distinction between different overloads of toString(): https://github.com/dlang/phobos/blob/master/std/format.d#L2805 - formatObject() calls put(), which finally uses the 'sink' parameter as an output range and fills in with characters. AliOn Monday, 7 November 2016 at 16:22:17 UTC, Heisenberg wrote:But how does it forward it if it's:Why? How can a delegate which returns nothing be used as an array which is going to be printed on the screen?You pass the string to the delegate, which does whatever with it somewhere else. So you call: `passed_delegate("your string");` and it can forward it to writeln or whatever.void delegate(const(char)[]) sink // ?It returns nothing, and just takes an array of characters as a parameter.. Just how does it print?..
Nov 07 2016
On Monday, 7 November 2016 at 16:54:06 UTC, Ali Çehreli wrote:On 11/07/2016 08:40 AM, Heisenberg wrote:I see now. :) Sounds like I have to finish the whole book to be able to grasp all of that a little better. I think I do get a general picture now, though. Thanks for a quick reply, everyone![...]Exactly how it happens requires explaining a long chain of function calls. Probably that's why the author did not elaborate further. ;) I've probably omitted some steps here but I think the following is close enough: - writeln() that's in the sample code calls write() with the addition of '\n'. - write() eventually calls formattedWrite() with a lockingTextWriter as the writer: https://github.com/dlang/phobos/blob/master/std/stdio.d#L1401 - formattedWrite() calls formatValue(): https://github.com/dlang/phobos/blob/master/std/format.d#L2906 - formatValue() calls formatObject() and that's the one that makes a distinction between different overloads of toString(): https://github.com/dlang/phobos/blob/master/std/format.d#L2805 - formatObject() calls put(), which finally uses the 'sink' parameter as an output range and fills in with characters. Ali
Nov 07 2016
On Monday, 7 November 2016 at 16:22:17 UTC, Heisenberg wrote:Hi there. I'm currently following Ali Çehreli's "Programming in D" book and I can't seem to be able to wrap my head around the of delegates in the toString() functions.. http://ddili.org/ders/d.en/lambda.html (Bottom of the page) Why? How can a delegate which returns nothing be used as an array which is going to be printed on the screen? And just how is it printed? The Documentation didn't make it much clearer.. the 'sink' is supposed to be the 'writer', isn't it? So what does it do? The arguments get interpreted and formatted into the string, but what happens after that? P.S. I've just checked the book, it doesn't seem to be explained anywhere properly (the way formattedRead is) P.P.S. I'm following the PDF version of the bookWhen you ask for a string (or more generally, an array), you put a specific construct on the way the data should be. For starters, the items have to be contiguous in memory. But most of the time, they aren't, and you end up allocating memory to put them contiguously. A sink is a method to work around that problem. A simple example would be: ``` import std.stdio; void main () { // A delegate that write to stdout auto dg = (const(char)[] v) { write(v); } ColoredPoint p; p.toString(dg); write("\n"); } ``` With this definition, every time `ColoredPoint` will want to output a chunk of data, it will call the delegate, which will print to stdout. When the function returns, we now all chunks have been appended, and we complete the line with a \n, flushing the output. This is great to write memory-friendly algorithm: ``` void main () { AppendBuffer!char myBuffer; auto dg = (const(char)[] v) { myBuffer ~= v; } ColoredPoint p; p.toString(dg); // Now we can use myBuffer.data } ``` With this example, you can see that, by just forwarding chunks to the delegate, we're able to let the caller to decide of the memory strategy to use. It can be plain GC allocation, it can be using `malloc` / `realloc`, it can use a static array and only aggregate up to a certain size, etc...
Nov 07 2016
On Monday, 7 November 2016 at 16:37:50 UTC, Mathias Lang wrote:On Monday, 7 November 2016 at 16:22:17 UTC, Heisenberg wrote:I see.. So we're basically adding stuff to it and then printing it all in one go. Still, the delegate in the example isn't defined, so how does it manage to output the result?[...]When you ask for a string (or more generally, an array), you put a specific construct on the way the data should be. For starters, the items have to be contiguous in memory. But most of the time, they aren't, and you end up allocating memory to put them contiguously. [...]
Nov 07 2016