Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
March 11, 2014 writeln if not empty | ||||
---|---|---|---|---|
| ||||
Attachments:
| Is there a way to do the following lazily: writelnIfNotEmpty(T)(T a){ auto b=text(a); if(b.length) writeln(b); } ie, without using std.conv.text (which needlessly computes an intermediate string, which could be quite large) or launching a separate process ? writelnIfNotEmpty(""); //doesn't print new line writelnIfNotEmpty("a"); //prints "a\n" |
March 11, 2014 Re: writeln if not empty | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timothee Cour | On 3/10/2014 9:24 PM, Timothee Cour wrote:
> Is there a way to do the following lazily:
>
> writelnIfNotEmpty(T)(T a){
> auto b=text(a);
> if(b.length)
> writeln(b);
> }
>
> ie, without using std.conv.text (which needlessly computes an intermediate
> string, which could be quite large) or launching a separate process ?
>
> writelnIfNotEmpty(""); //doesn't print new line
> writelnIfNotEmpty("a"); //prints "a\n"
>
Sounds like what you need is a version of to!string() or text() that takes an output sink. Taking a look at std.conv, I'm kinda surprised I don't see one :/
|
March 11, 2014 Re: writeln if not empty | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nick Sabalausky | On Monday, March 10, 2014 21:50:25 Nick Sabalausky wrote:
> On 3/10/2014 9:24 PM, Timothee Cour wrote:
> > Is there a way to do the following lazily:
> >
> > writelnIfNotEmpty(T)(T a){
> > auto b=text(a);
> > if(b.length)
> >
> > writeln(b);
> >
> > }
> >
> > ie, without using std.conv.text (which needlessly computes an intermediate string, which could be quite large) or launching a separate process ?
> >
> > writelnIfNotEmpty(""); //doesn't print new line
> > writelnIfNotEmpty("a"); //prints "a\n"
>
> Sounds like what you need is a version of to!string() or text() that
> takes an output sink. Taking a look at std.conv, I'm kinda surprised I
> don't see one :/
std.format.formattedWrite will do the equivalent of writef to an output range, though I'm not sure that that will really do what the OP wants, since it would still have to write the result to an output range even if it were empty, and odds are that the output range would be something on the heap anyway (e.g. Appender is technically on the stack, but it's contents are on the heap), making it so that it probably doesn't help much in this case.
Though to be honest, I'm not quite sure why writelnIfNotEmpty would be very useful unless what's being passed in would result in the empty string, and I would think that that would almost always be detectable (the one exception being user-defined types whose toString results in an empty string). Something as simple as
void writelnIfNotEmpty(T)(T a)
{
static if(isInputRange!T)
{
if(!a.empty)
writeln(a);
}
else
writeln(a);
}
would then cover most cases - the one exception being toStrings which can result in empty. And if that's a concern, then something like
else static if(is(T == struct) || is(T == class))
{
auto b = to!string(a);
if(b.length)
writeln(b);
}
should take care of the toString case. It doesn't avoid creating an intermediate string, but unless the toString takes an output range, it's always going to allocate anyway, and if it does take an output range, once again, you'd need one which somehow avoided allocating altogether, which isn't particularly likely.
Alternatively, you could just assume that no toString will result in the empty string, as it's probably pretty rare that it would, but I'm not sure that that would actually save you any overhead except in the case where the toString takes an output range (since otherwise, it'll allocate a new string regardless), but toStrings which take output ranges are fairly uncommon at this point.
- Jonathan M Davis
|
March 11, 2014 Re: writeln if not empty | ||||
---|---|---|---|---|
| ||||
Attachments:
| On Mon, Mar 10, 2014 at 9:14 PM, Jonathan M Davis <jmdavisProg@gmx.com>wrote: > On Monday, March 10, 2014 21:50:25 Nick Sabalausky wrote: > > On 3/10/2014 9:24 PM, Timothee Cour wrote: > > > Is there a way to do the following lazily: > > > > > > writelnIfNotEmpty(T)(T a){ > > > auto b=text(a); > > > if(b.length) > > > > > > writeln(b); > > > > > > } > > > > > > ie, without using std.conv.text (which needlessly computes an > intermediate > > > string, which could be quite large) or launching a separate process ? > > > > > > writelnIfNotEmpty(""); //doesn't print new line > > > writelnIfNotEmpty("a"); //prints "a\n" > > > > Sounds like what you need is a version of to!string() or text() that > > takes an output sink. Taking a look at std.conv, I'm kinda surprised I > > don't see one :/ > > std.format.formattedWrite will do the equivalent of writef to an output > range, > though I'm not sure that that will really do what the OP wants, since it > would > still have to write the result to an output range even if it were empty, > and > odds are that the output range would be something on the heap anyway (e.g. > Appender is technically on the stack, but it's contents are on the heap), > making it so that it probably doesn't help much in this case. > > Though to be honest, I'm not quite sure why writelnIfNotEmpty would be very > useful unless what's being passed in would result in the empty string, and > I > would think that that would almost always be detectable (the one exception > being user-defined types whose toString results in an empty string). > Something > as simple as > > void writelnIfNotEmpty(T)(T a) > { > static if(isInputRange!T) > { > if(!a.empty) > writeln(a); > } > else > writeln(a); > } > > would then cover most cases - the one exception being toStrings which can result in empty. And if that's a concern, then something like > > else static if(is(T == struct) || is(T == class)) > { > auto b = to!string(a); > if(b.length) > writeln(b); > } > > should take care of the toString case. It doesn't avoid creating an > intermediate string, but unless the toString takes an output range, it's > always going to allocate anyway, and if it does take an output range, once > again, you'd need one which somehow avoided allocating altogether, which > isn't > particularly likely. > > Alternatively, you could just assume that no toString will result in the > empty > string, as it's probably pretty rare that it would, but I'm not sure that > that > would actually save you any overhead except in the case where the toString > takes an output range (since otherwise, it'll allocate a new string > regardless), but toStrings which take output ranges are fairly uncommon at > this point. > > - Jonathan M Davis > Thanks, that indeed works in many cases but there are still others (eg what if empty() prints, say in debug mode: writelnIfNotEmpty would not print the debug information even though writeln() would execute that debug code and print something). I was wondering whether there would be a robust way that would check whether anything was written to stdout (eg via accessing raw file pointer/C lib), eg something like that : void writelnIfNotEmpty(T)(T a){ auto file_pos=get_filepos(stdin); write(a); if(get_filepos(stdin)!=file_pos) writeln; } |
March 11, 2014 Re: writeln if not empty | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timothee Cour | On Tuesday, 11 March 2014 at 05:37:25 UTC, Timothee Cour wrote:
> void writelnIfNotEmpty(T)(T a){
> auto file_pos=get_filepos(stdin);
> write(a);
> if(get_filepos(stdin)!=file_pos)
> writeln;
> }
You could simply create an sink that forwards to stdout, while keeping state:
//----
import std.stdio, std.format;
bool writelnIfNotEmpty(T)(T a)
{
bool written = false;
void checkWriter(in char[] s)
{
if (s.length)
{
written = true;
write(s);
}
}
formattedWrite(&checkWriter, "%s", a);
if (written)
{
writeln();
return true;
}
return false;
}
void main()
{
writelnIfNotEmpty(1);
writelnIfNotEmpty("");
writelnIfNotEmpty(2);
}
//----
This prints:
//----
1
2
//----
Also, this didn't work up until a few releases ago. I am really really happy to see code like this finally "just work". yay!
|
March 11, 2014 Re: writeln if not empty | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timothee Cour | On Tuesday, 11 March 2014 at 05:37:25 UTC, Timothee Cour wrote:
> Thanks,
> that indeed works in many cases but there are still others (eg what if
> empty() prints, say in debug mode: writelnIfNotEmpty would not print the
> debug information even though writeln() would execute that debug code and
> print something).
>
> I was wondering whether there would be a robust way that would check
> whether anything was written to stdout (eg via accessing raw file pointer/C
> lib)
I have no idea how you could intercept such information. At least, not with "standard D". You'd (probably) have to, as you said, have to dig up C's stdlib.
|
March 12, 2014 Re: writeln if not empty | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra Attachments:
| Thanks! your solution is more robust (minus some caveats i mentioned) and also trivially extends to variadics.
On Tue, Mar 11, 2014 at 2:07 PM, monarch_dodra <monarchdodra@gmail.com>wrote:
> On Tuesday, 11 March 2014 at 05:37:25 UTC, Timothee Cour wrote:
>
>> void writelnIfNotEmpty(T)(T a){
>> auto file_pos=get_filepos(stdin);
>> write(a);
>> if(get_filepos(stdin)!=file_pos)
>> writeln;
>> }
>>
>
> You could simply create an sink that forwards to stdout, while keeping state:
>
> //----
> import std.stdio, std.format;
>
> bool writelnIfNotEmpty(T)(T a)
> {
> bool written = false;
> void checkWriter(in char[] s)
> {
> if (s.length)
> {
> written = true;
> write(s);
> }
> }
> formattedWrite(&checkWriter, "%s", a);
> if (written)
> {
> writeln();
> return true;
> }
> return false;
> }
>
> void main()
> {
> writelnIfNotEmpty(1);
> writelnIfNotEmpty("");
> writelnIfNotEmpty(2);
> }
> //----
>
> This prints:
> //----
> 1
> 2
> //----
>
> Also, this didn't work up until a few releases ago. I am really really happy to see code like this finally "just work". yay!
>
|
Copyright © 1999-2021 by the D Language Foundation