www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Question about OutputRange and std.range: put

reply Uranuz <neuranuz gmail.com> writes:
Hello to forum readers!
I have a question about using OutputRange and std.range: put. I 
have the following code snippet to illustrate my question:

import std.range, std.stdio, std.string;

void main()
{
	string greating = "Hello, "	;
	string username = "Bob";
	
	put(greating, username);
	put(greating, "!");
	
	writeln(greating);
}

It doesn't compile. It says following:

Compilation output:
/opt/compilers/dmd2/include/std/range/primitives.d(335): Error: 
static assert  "Cannot put a string into a string."
/d52/f530.d(8):        instantiated from here: put!(string, 
string)

Documentation about std.range.put says that one of code snippets, 
where *put* applies is when second argument is array of elements 
(If I understand it correctly with my knowledge of English). 
Especially the following line is there in doc for usage scenario:

r.doPut([ e ]);	R specifically accepts an E[].

Can I put range or array f elements as second argument of put or 
it works only at per element basis? I think writing multiple 
items at a time should be more effective but maybe it's not 
supported for OutputRange? Could someone explain?
Jan 16 2016
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, January 16, 2016 12:11:11 Uranuz via Digitalmars-d-learn wrote:
 Hello to forum readers!
 I have a question about using OutputRange and std.range: put. I
 have the following code snippet to illustrate my question:

 import std.range, std.stdio, std.string;

 void main()
 {
   string greating = "Hello, " ;
   string username = "Bob";

   put(greating, username);
   put(greating, "!");

   writeln(greating);
 }

 It doesn't compile. It says following:

 Compilation output:
 /opt/compilers/dmd2/include/std/range/primitives.d(335): Error:
 static assert  "Cannot put a string into a string."
 /d52/f530.d(8):        instantiated from here: put!(string,
 string)

 Documentation about std.range.put says that one of code snippets,
 where *put* applies is when second argument is array of elements
 (If I understand it correctly with my knowledge of English).
 Especially the following line is there in doc for usage scenario:

 r.doPut([ e ]);   R specifically accepts an E[].

 Can I put range or array f elements as second argument of put or
 it works only at per element basis? I think writing multiple
 items at a time should be more effective but maybe it's not
 supported for OutputRange? Could someone explain?
There are a few problems here. First off, when put is used with an array, it fills the array. It doesn't append to it. So, you can't use a string as an output range, since its elements are immutable. Here's an example with int[]: auto arr = new int[](5); assert(arr == [0, 0, 0, 0, 0]); auto output = arr; put(output, 1); assert(arr == [1, 0, 0, 0, 0]); assert(output == [0, 0, 0, 0]); put(output, [2, 3, 4]); assert(arr == [1, 2, 3, 4, 0]); assert(output == [0]); Add yes, put will take either an element or a range of elements - which is why it's generally a bad idea to use put with UFCS (because an overloaded put is unlikely to have all of the overloads that the free function supports, but it will be used by the free function where appropriate). Now, characters get a bit more interesting due to Unicode concerns. Narrow strings - i.e. arrays of char or wchar - are not considered output ranges, similar to how hasLength!string will return false (since with char (UTF-8) and wchar (UTF-16) a single code point will not necessarily fit within a single code unit, whereas with dchar (UTF-32), they're one and the same). Whether that's how it _should_ work is debatable - particularly since the main reason to not allow it is because with a short enough char[] or wchar[], you can't guarantee that a dchar will fit, but you can't guarantee that an input range of characters will fit either (and yet put accepts those). But regardless, char[] and wchar[] are not currently considered output ranges. So, if you want to use an array of characters as an output range, it'll have to be dchar[]. Now, odds are what you really want is to use Appender. It _does_ append when inserting elements rather than fill, and it _can_ be used with narrow strings - including string. e.g. auto output = appender!string(); assert(output.data.empty); output.put("hello"); assert(output.data == "hello"); - Jonathan M Davis
Jan 16 2016
parent reply Uranuz <neuranuz gmail.com> writes:
On Saturday, 16 January 2016 at 16:14:56 UTC, Jonathan M Davis 
wrote:
 On Saturday, January 16, 2016 12:11:11 Uranuz via 
 Digitalmars-d-learn wrote:
 [...]
There are a few problems here. First off, when put is used with an array, it fills the array. It doesn't append to it. So, you can't use a string as an output range, since its elements are immutable. Here's an example with int[]: [...]
Thanks for your response. After looking for some time in Phobos documentation I already found that it would be better to use Appender for this purpose. As far as I understand it allocates less often and I can reserve some memory from the start. I'll take this possibility into account when I'll do further small optimisations. Thanks again.
Jan 16 2016
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, January 16, 2016 16:53:24 Uranuz via Digitalmars-d-learn wrote:
 On Saturday, 16 January 2016 at 16:14:56 UTC, Jonathan M Davis
 wrote:
 On Saturday, January 16, 2016 12:11:11 Uranuz via
 Digitalmars-d-learn wrote:
 [...]
There are a few problems here. First off, when put is used with an array, it fills the array. It doesn't append to it. So, you can't use a string as an output range, since its elements are immutable. Here's an example with int[]: [...]
Thanks for your response. After looking for some time in Phobos documentation I already found that it would be better to use Appender for this purpose. As far as I understand it allocates less often and I can reserve some memory from the start. I'll take this possibility into account when I'll do further small optimisations. Thanks again.
Where using an array rather than Appender makes sense is when you want to allocate a buffer and the reuse it over and over again rather than allocating a new array and then growing it efficiently each time. But then you have to be careful about how you use it, since arrays are quirkier when using them as output ranges - and unlike Appender, they can fill up. So, it's a question of what you're trying to do and what's reasonable in your program. - Jonathan M Davis
Jan 16 2016