Thread overview
string to thread
Sep 08, 2012
monarch_dodra
Sep 08, 2012
Jonathan M Davis
Sep 09, 2012
monarch_dodra
Sep 09, 2012
Jonathan M Davis
Sep 09, 2012
monarch_dodra
Sep 09, 2012
Jonathan M Davis
Apr 23, 2015
armando sano
September 08, 2012
In C++ (not C), when you wanted to parse a string, you were supposed to put the string inside a stream (std::stringstream), and then parse that new stream stream.

As a general rule, stringstream also allowed abstracting a string into a more generic stream.

I did not find anything equivalent in D. Did I just miss it?

How is a user supposed to parse a string in D?

I thought I was supposed to use "formattedRead", but was told that was actually more meant for library writers. Is there an alternative? Just trying to learn.
September 08, 2012
On Saturday, September 08, 2012 11:01:50 monarch_dodra wrote:
> In C++ (not C), when you wanted to parse a string, you were
> supposed to put the string inside a stream (std::stringstream),
> and then parse that new stream stream.
> 
> As a general rule, stringstream also allowed abstracting a string into a more generic stream.
> 
> I did not find anything equivalent in D. Did I just miss it?
> 
> How is a user supposed to parse a string in D?
> 
> I thought I was supposed to use "formattedRead", but was told that was actually more meant for library writers. Is there an alternative? Just trying to learn.

If you were to operate on a string in a manner similar to a stream, you'd be operating on it as a range, and there are a lot of range-based functions in Phobos. But if you want to specifically parse a range of characters, then use std.conv.parse:

http://dlang.org/phobos/std_conv.html#parse

If you want to know more about ranges, then this is currently the best tutorial on them:

http://ddili.org/ders/d.en/ranges.html

- Jonathan M Davis
September 09, 2012
On Saturday, 8 September 2012 at 09:10:30 UTC, Jonathan M Davis wrote:
>
> If you were to operate on a string in a manner similar to a stream, you'd be
> operating on it as a range, and there are a lot of range-based functions in
> Phobos. But if you want to specifically parse a range of characters, then use
> std.conv.parse:
>
> http://dlang.org/phobos/std_conv.html#parse
>
> If you want to know more about ranges, then this is currently the best
> tutorial on them:
>
> http://ddili.org/ders/d.en/ranges.html
>
> - Jonathan M Davis

Hum, parse. Looks useful. No need to create a temp stream like in C++ then. Good. Thanks for the info.

That said, is the "abstraction" itself available? Say *someone* wrote an xml parser, and the public interface expects to operate on a "File". Now, supposing I don't have a file, but all I have is a string, is it possible for someone pass that string the the function?
September 09, 2012
On Sunday, September 09, 2012 12:21:59 monarch_dodra wrote:
> Hum, parse. Looks useful. No need to create a temp stream like in C++ then. Good. Thanks for the info.
> 
> That said, is the "abstraction" itself available? Say *someone* wrote an xml parser, and the public interface expects to operate on a "File". Now, supposing I don't have a file, but all I have is a string, is it possible for someone pass that string the the function?

So, your asking if you can pass a string to a function which requires a file? Is that what you're asking? I don't see why that would work, given that the function expects a file. So, I must be misunderstanding something...

But one of the key design philosophies in Phobos is to use range-based APIs for just about everything. So, we wouldn't have an XML parser which operated on a file. It would operate on a range, and if you wanted to operate on a file, you'd get a range over the file and pass it in rather than the file. That's actually one key area that std.io should improve over std.stdio once Steven finally finishes it (since right now, it's kind of hard to have a good range interface on a file - you've can operate on it as a range of lines or chunks or whatnot but not as a range of characters or bytes without creating some sort of wrapper). But by using ranges everywhere, it becomes a lot like the *nix command-line where you can take the output of any program and pipe it as input to another program, allowing you to chain programs and mix and match them, but it's with ranges and functions rather than pipes and programs.

- Jonathan M Davis
September 09, 2012
On Sunday, 9 September 2012 at 10:36:14 UTC, Jonathan M Davis wrote:
> On Sunday, September 09, 2012 12:21:59 monarch_dodra wrote:
>> Hum, parse. Looks useful. No need to create a temp stream like in
>> C++ then. Good. Thanks for the info.
>> 
>> That said, is the "abstraction" itself available? Say *someone*
>> wrote an xml parser, and the public interface expects to operate
>> on a "File". Now, supposing I don't have a file, but all I have
>> is a string, is it possible for someone pass that string the the
>> function?
>
> So, your asking if you can pass a string to a function which requires a file?
> Is that what you're asking? I don't see why that would work, given that the
> function expects a file. So, I must be misunderstanding something...

No, not like that, that'd be stupid.

In C++, a functions that can parse/unparse (serialize/deserialize) take a "stream" as an argument. That is c++'s paradigm. A stream, more often than not, is a filestream, or a standard input/output stream.

Now every once in a while, you'd like to parse/print a string. To do this, you create a "stringstream". That's what I was asking about.

More below:

> But one of the key design philosophies in Phobos is to use range-based APIs
> for just about everything. So, we wouldn't have an XML parser which operated
> on a file. It would operate on a range, and if you wanted to operate on a file,
> you'd get a range over the file and pass it in rather than the file. That's
> actually one key area that std.io should improve over std.stdio once Steven
> finally finishes it (since right now, it's kind of hard to have a good range
> interface on a file - you've can operate on it as a range of lines or chunks or
> whatnot but not as a range of characters or bytes without creating some sort
> of wrapper). But by using ranges everywhere, it becomes a lot like the *nix
> command-line where you can take the output of any program and pipe it as input
> to another program, allowing you to chain programs and mix and match them, but
> it's with ranges and functions rather than pipes and programs.
>
> - Jonathan M Davis

Okay, that makes a sense to me. In c++, the paradigm is:
*Streams for formating.
*Iterators for algorithms.

Two paradigms => object to go from string(pointer/iterator) to stream.

And you are telling me that in D, everything is ranges, so there is no such need for a string stream. Everything operates on ranges, and you can (or should be able) to scare an range out of a stream, is this correct?

--------
What bothers me though (and is the source of my confusion) is that in that case, shouldn't stdin and stdout also be ranges? Or at least, shouldn't there be a global equivalent symbol that is a range?

Imagine teaching a program that asks a user his age, and prints it. This is what you would teach: (qualified names for making a point)

--------
import std.stdio;
void main()
{
    int age;
    //What is your age?
    std.stdio.stdin.readf("%s", &age);
    std.stdio.stdout.writefln("Your age is %s", age);
}
--------

Now, the next logical step in the lesson, is to make things more generic with a function, right? Why stop at std[in|out]?

--------
import std.stdio;
import std.array;
import std.format;

void askAge(RangeIn, RangeOut)(RangeIn ri, RangeOut ro)
{
    int age;
    //What is your age?
    ri.formattedRead("%s", &age); //This changed
    ro.formattedWrite("Your age is %s\n", age); //So did this

}

void main()
{
    auto input = "42";
    auto output = appender!string();
    askAge(input, output);
    writeln(output.data);
}
--------

Instead of using std[in|out], I create an InputRange (a string), and an output range (an Appender!string). However, the methods readf/writefln have been changed!

This is what is confusing me: I have been taught to work on streams, but I can't use that interface on strings/ranges :/
September 09, 2012
On Sunday, September 09, 2012 14:13:26 monarch_dodra wrote:
> Okay, that makes a sense to me. In c++, the paradigm is:
> *Streams for formating.
> *Iterators for algorithms.
> 
> Two paradigms => object to go from string(pointer/iterator) to
> stream.
> 
> And you are telling me that in D, everything is ranges, so there is no such need for a string stream. Everything operates on ranges, and you can (or should be able) to scare an range out of a stream, is this correct?

Pretty much. Some functions will never operate on ranges, and in some cases it may make sense to have a way to do something with ranges and a way to do it without them, but in general, things are going to be range based, and we may not even _have_ streams, since a stream is effectively a range.

> What bothers me though (and is the source of my confusion) is that in that case, shouldn't stdin and stdout also be ranges? Or at least, shouldn't there be a global equivalent symbol that is a range?

More likely, there will be a way to get a range over stdin or stdout. That's pretty much how we deal with it now (stdin and stdout are Files and get whatever functions File has). It's just that we need a better range type on Files (something which is forward range of bytes or characters rather than ranges of lines or chunks). That'll come with std.io, but Steven has been too busy to finish it yet.

> Instead of using std[in|out], I create an InputRange (a string),
> and an output range (an Appender!string). However, the methods
> readf/writefln have been changed!

I'd point out that formattedRead and formattedWrite aren't really meant to be used directly in your typical program, so they're not a terribly great example. Regardless, _some_ functions will make more sense to _not_ be range- based, and even if something works better if it's range-based, some older stuff which was created before we started using ranges is going to stick around for backwards compatibility if nothing else. So, some things may get range-based replacements but still have the old versions around. Exactly what happens with input and output will depend heavily on what happens with std.io.

> This is what is confusing me: I have been taught to work on streams, but I can't use that interface on strings/ranges :/

I don't understand this remark. You mean that the ranges for operating on input and output properly don't exist yet? That's definitely true, but it will be fixed. If you mean something else, I don't know what you mean. Streams pretty much _are_ ranges.

- Jonathan M Davis
April 23, 2015
Is there any update on this? This question of the distinction between reading/writing to a file stream vs to a string seems recurrent.

I am interested in writing to a string and am wondering if there is a reason for having to use explicitly the convenience functions std.conv.text() (or to!string()) and std.string.format() when one would expect to be able to do:

string s;
int n = 10;
s.writeln("Hello ", n, " times"); // s == "Hello 10 times\n"
s.writefln("and %d times", n); // s == "Hello 10 times\nand 10 times\n"

There is a simple way to emulate this by expanding on string:

struct sstring {
	string _s;
	alias _s this; // treat sstring just like _s, a string
	
	this(string literal = "") { _s = literal; }

	void write(Args...)(Args args) { foreach(a; args)  {_s ~= to!string(a);} }
	void writeln(Args...)(Args args) { this.write(args, '\n'); }
	void writef(Char, Args...)(in Char[] fmt, Args args) { _s ~= std.string.format(fmt, args); }
	void writefln(Char, Args...)(in Char[] fmt, Args args) { this.writef(fmt ~ '\n', args); }	
}

An 'sstring s;' can be then be used just like a normal 'string' with the addition of being able to use it as in the example above.

I suspect something similar could be done for reading.

Why the syntactic burden of having to use to!string, std.string.format, std.conv.parse etc?