February 11, 2007
Lars Ivar Igesund wrote:
> Kevin Bealer wrote:
> 
>> Lars Ivar Igesund wrote:
>>> Frits van Bommel wrote:
>>>
>>>> Which one to use is hard to say at this point. I've been trying out
>>>> Tango since its release and I like it but I sometimes miss some parts of
>>>> Phobos. Whether this is because Phobos is just more familiar to me or
>>>> actually better is hard to say...
>>> Note that what you miss that you feel you have in Phobos, is very much
>>> part of the feedback we would like.
>>>
>> Okay -- I'm really sorry if any of this seems to have a negative tone.
>> I hesitate to write this since I have a lot of respect for the Tango
>> design in general, but there are a couple of friction points I've noticed.
>>
>> 1. writefln / format replacements
>>
>> Concerning standard output and string formatting, in phobos I can do
>> these operations:
>>
>>    writefln("%s %s %s", a, b, c);
>>    format("%s %s %s", a, b, c);
>>
>> How do I do these in Tango?  The change to "{0} {1}" stuff is fine with
>> me, in fact I like it, but this syntax:
>>
>>    Stdout.formatln("{0} {1} {2}", a, b, c);
>>    Format!(char).convert("{0} {1} {2}", a, b, c);
> 
> I can't give you an immediate solution for the "length" of the
> Stdout.formatln, but usually one would use tango.text.convert.Sprint for
> what you use the formatter for. If you only want to print values, there are
> non-formatting ways to do that. We may be able to give you quicker/easier
> help over our forum if you have followups? May make it easier for us to
> integrate solutions into our documentation too if needed.
> 

In the future I'll post this kind of thing on the tango forum, but since this thread is already here:

I looked at this but doesn't Sprint require something like:

    char[] foo = (new Sprint!(char))("hello {0}", 123);

1. A template instance is needed.

2. It creates a new class object (Sprint) -- you can't use a single
   global one in real code because according to the docs it owns the
   internal buffer and is thus not thread safe.

3. That object contains a new buffer which must be allocated.

4. As far as I can tell, it requires almost as much syntax as
   Format!(char) ( my previous example didn't include the 'new'
   as shown here: )

    char[] one = (new Format!(char)).convert("{0}", 123);
    char[] foo = (new Sprint!(char))("hello {0}", 123);

I haven't looked at the design of std.format but I would think it only suffers from #3 above, which is not too surprising since you would expect a string generation to require a new buffer.

Sprint seems like a good solution in something like an XML formatting loop, because you can build one object and its in a function call so the thread safety issue is not critical, you can reuse it many times, but as a drop in for 'std.format' it seemed kind of awkward to me.

Kevin
February 11, 2007
Sean Kelly wrote:
> Kevin Bealer wrote:
>>
>> Okay -- I'm really sorry if any of this seems to have a negative tone. I hesitate to write this since I have a lot of respect for the Tango design in general, but there are a couple of friction points I've noticed.
>>
>> 1. writefln / format replacements
>>
>> Concerning standard output and string formatting, in phobos I can do these operations:
>>
>>   writefln("%s %s %s", a, b, c);
>>   format("%s %s %s", a, b, c);
>>
>> How do I do these in Tango?  The change to "{0} {1}" stuff is fine with me, in fact I like it, but this syntax:
>>
>>   Stdout.formatln("{0} {1} {2}", a, b, c);
>>   Format!(char).convert("{0} {1} {2}", a, b, c);
>>
>> Is awkward.  And these statements are used *all the time*.  In a recent toy project I wrote, I used Stdout 15 times, compared to using "foreach" only 8 times.  I also use the "format to string" idiom a lot (oddly enough, not in that project), and it's even more awkward.
> 
> The conversion modules seem to have slightly spotty API documentation, but I think this will work for the common case:
> 
> Formatter( "{0} {1} {2}", a, b, c );

Okay, I didn't see this possibility, that actually looks like a decent syntax; I withdraw the paragraphs in question, subject to the (zig zag) example below. :)

> The Stdout design is the result of a lengthy discussion involving overload rules and expected behavior.  I believe two of the salient points were that the default case should be the simplest to execute, and that the .format method call provided a useful signifier that an explicit format was being supplied.  That said, I believe that the default output format can be called via:
> 
> Stdout( a, b, c );
> 
> or the "whisper" syntax:
> 
> Stdout( a )( b )( c );

Okay - there is a problem with new users who try to print strings with "%" somewhere in the string -- this solves that problem, which is nice.

>> That's why I think phobos really did the "Right Thing" by keeping those down to one token.  Second, the fact that the second one does exactly what the first does but you need to build a template, etc, is annoying.  I kept asking myself if I was doing the right thing because it seemed like I was using too much syntax for this kind of operation (I'm still not sure it's the best way to go -- is it?)

So am I, but in D I often don't have to, maybe I'm getting spoiled.

> Do you consider the Formatter instance to be sufficient or would it be more useful to wrap this behavior in a free function?  I'll admit that, being from a C++ background I'm quite used to customizing the library behavior to suit my particular use style, but I can understand the desire for "out of the box" convenience.

Hmmm.... given these two statements:

1. char[] zig = Formatter("{0} {1}", "ciao", "bella");
2. char[] zag = Formatter("{0} {1}", "one", "two");

Questions:

A. If these are done sequentially, will zig be affected by the processing of 'zag'?  (I.e. because of buffer sharing.)

B. Will doing 1 and 2 from different threads affect zig or zag?

If the answer to A and B is both "NO", then I have no problem with using Formatter.  I don't care about free function specifically (i.e. for getting a pointer or something), I just want safety, efficiency and clean syntax.

Documentation for Sprint suggests that both 1 and 2 are dangerous, I don't know if Formatter is like Sprint in that regard.

>> 2. toString and toUtf8 (collisions)
>>
>> The change of the terminology is actually okay with me.
>>
>> But phobos has a way of using toString as both a method and a top-level function name, all over the place.  This gets really clumsy because you can never use the top level function names when writing a class unless you fully qualify them.
>>
>> For example, std.cpuid.toString(), always has to be fully qualified when called from a class, and seems nondescriptive anyway.  All the std.conv.toString() functions are nice but it's easy to accidentally call the in-class toString() by accident.
>>
>> For the utf8 <--> utf16 and similar, it's frustrating to have to do this:
>>
>> dchar[] x32 = ...;
>> char[] x8 = tango.text.convert.Utf.toUtf8(x32);
>>
>> But you have to fully qualify if you are writing code in any class or struct.  If these were given another name, like makeUtf8, then these collisions would not happen.
> 
> One aspect of the Mango design that has carried forward into Tango is that similar functions are typically intended to live in their own namespace for the sake of clarity.  Previously, most/all of the free functions were declared in structs simply to prevent collisions, but this had code bloat issues so the design was changed.  Now, users are encouraged to use the aliasing import to produce the same effect:
> 
> import Utf = tango.text.convert.Utf;
> 
> Utf.toUtf8( x32 );
> 
> I'll admit it's not as convenient as simply importing and using the functions, but it does make the origin of every function call quite clear.  I personally avoid "using" in C++ for exactly this reason--if I'm using an external routine I want to know what library it's from by inspection.
> 
> 
> Sean

This is not earth-shaking to me, so the current way is not a big deal, but what I want to avoid is what I think of as the Java naming effect, where you need to do this:

System.out.print(foo);

... to print something.  To me, the design of a programming language or library is like a natural language.  In english we say "tin can" but we always say "can" when there is no ambiguity.  You never say "I want to buy a tin can of beans".  (I think that in the UK, they say "tin of beans" instead, but its the same idea.)

My view is for the common things to be simple and the complex things to be as simple as possible.  The extra formality of spelling out the full names of things is something that people find comfort in (*), but I would as soon do without in D.

(*) I think people find comfort in it because they have been abused by other languages.  In C and C++ land, I agree --- if you do a '#define binary 1' in an include file somewhere, you can kill an algorithm in another file that is a dozen includes up the chain -- I found exactly this definition in a file at my job, and it was an 'interesting' problem to debug.  Working on large C and C++ projects breeds a kind of paranoia about symbol tables that I can completely relate to.

Sometimes the combination of #include and #define is a lot like "come from" in the way that it messes with the debugging process.

http://en.wikipedia.org/wiki/Come_from

But again, sorry if I'm being nit picky.

Kevin

February 11, 2007
Kevin Bealer wrote:
> Lars Ivar Igesund wrote:
> 
>> Kevin Bealer wrote:
>>
>>> Lars Ivar Igesund wrote:
>>>
>>>> Frits van Bommel wrote:
>>>>
>>>>> Which one to use is hard to say at this point. I've been trying out
>>>>> Tango since its release and I like it but I sometimes miss some parts of
>>>>> Phobos. Whether this is because Phobos is just more familiar to me or
>>>>> actually better is hard to say...
>>>>
>>>> Note that what you miss that you feel you have in Phobos, is very much
>>>> part of the feedback we would like.
>>>>
>>> Okay -- I'm really sorry if any of this seems to have a negative tone.
>>> I hesitate to write this since I have a lot of respect for the Tango
>>> design in general, but there are a couple of friction points I've noticed.
>>>
>>> 1. writefln / format replacements
>>>
>>> Concerning standard output and string formatting, in phobos I can do
>>> these operations:
>>>
>>>    writefln("%s %s %s", a, b, c);
>>>    format("%s %s %s", a, b, c);
>>>
>>> How do I do these in Tango?  The change to "{0} {1}" stuff is fine with
>>> me, in fact I like it, but this syntax:
>>>
>>>    Stdout.formatln("{0} {1} {2}", a, b, c);
>>>    Format!(char).convert("{0} {1} {2}", a, b, c);
>>
>>
>> I can't give you an immediate solution for the "length" of the
>> Stdout.formatln, but usually one would use tango.text.convert.Sprint for
>> what you use the formatter for. If you only want to print values, there are
>> non-formatting ways to do that. We may be able to give you quicker/easier
>> help over our forum if you have followups? May make it easier for us to
>> integrate solutions into our documentation too if needed.
>>
> 
> In the future I'll post this kind of thing on the tango forum, but since this thread is already here:
> 
> I looked at this but doesn't Sprint require something like:
> 
>     char[] foo = (new Sprint!(char))("hello {0}", 123);
> 
> 1. A template instance is needed.
> 
> 2. It creates a new class object (Sprint) -- you can't use a single
>    global one in real code because according to the docs it owns the
>    internal buffer and is thus not thread safe.
> 
> 3. That object contains a new buffer which must be allocated.
> 
> 4. As far as I can tell, it requires almost as much syntax as
>    Format!(char) ( my previous example didn't include the 'new'
>    as shown here: )
> 
>     char[] one = (new Format!(char)).convert("{0}", 123);
>     char[] foo = (new Sprint!(char))("hello {0}", 123);


Inside Format.d, there's a static instance called Formatter. This is what's used by Stdout, so it will remain in the library. Thus it's legit to use that instead of new Format!(char)  ...  e.g. your example becomes

#  auto one = Formatter ("{0}", 123);

There's also a sprint() method on the Format instance, so you can do this where you need to avoid heap activity altogether:

#  char[16] tmp;
#  auto one = Formatter.sprint (tmp, "{0}", 123);

Instantiating templates is fine from the perspective of something like a server, but it's definately too noisy/verbose for something like pedestrian usage; as you have been pointing out. Sean gave some examples of wrappers in a post today, which may prove fruitful.


> 
> I haven't looked at the design of std.format but I would think it only suffers from #3 above, which is not too surprising since you would expect a string generation to require a new buffer.
> 
> Sprint seems like a good solution in something like an XML formatting loop, because you can build one object and its in a function call so the thread safety issue is not critical, you can reuse it many times, but as a drop in for 'std.format' it seemed kind of awkward to me.

Probably :)

February 11, 2007
Kevin Bealer wrote:
> Sean Kelly wrote:
> 
>> Kevin Bealer wrote:
>>
>>>
>>> Okay -- I'm really sorry if any of this seems to have a negative tone. I hesitate to write this since I have a lot of respect for the Tango design in general, but there are a couple of friction points I've noticed.
>>>
>>> 1. writefln / format replacements
>>>
>>> Concerning standard output and string formatting, in phobos I can do these operations:
>>>
>>>   writefln("%s %s %s", a, b, c);
>>>   format("%s %s %s", a, b, c);
>>>
>>> How do I do these in Tango?  The change to "{0} {1}" stuff is fine with me, in fact I like it, but this syntax:
>>>
>>>   Stdout.formatln("{0} {1} {2}", a, b, c);
>>>   Format!(char).convert("{0} {1} {2}", a, b, c);
>>>
>>> Is awkward.  And these statements are used *all the time*.  In a recent toy project I wrote, I used Stdout 15 times, compared to using "foreach" only 8 times.  I also use the "format to string" idiom a lot (oddly enough, not in that project), and it's even more awkward.
>>
>>
>> The conversion modules seem to have slightly spotty API documentation, but I think this will work for the common case:
>>
>> Formatter( "{0} {1} {2}", a, b, c );
> 
> 
> Okay, I didn't see this possibility, that actually looks like a decent syntax; I withdraw the paragraphs in question, subject to the (zig zag) example below. :)
> 
>> The Stdout design is the result of a lengthy discussion involving overload rules and expected behavior.  I believe two of the salient points were that the default case should be the simplest to execute, and that the .format method call provided a useful signifier that an explicit format was being supplied.  That said, I believe that the default output format can be called via:
>>
>> Stdout( a, b, c );
>>
>> or the "whisper" syntax:
>>
>> Stdout( a )( b )( c );
> 
> 
> Okay - there is a problem with new users who try to print strings with "%" somewhere in the string -- this solves that problem, which is nice.
> 
>>> That's why I think phobos really did the "Right Thing" by keeping those down to one token.  Second, the fact that the second one does exactly what the first does but you need to build a template, etc, is annoying.  I kept asking myself if I was doing the right thing because it seemed like I was using too much syntax for this kind of operation (I'm still not sure it's the best way to go -- is it?)
> 
> 
> So am I, but in D I often don't have to, maybe I'm getting spoiled.
> 
>> Do you consider the Formatter instance to be sufficient or would it be more useful to wrap this behavior in a free function?  I'll admit that, being from a C++ background I'm quite used to customizing the library behavior to suit my particular use style, but I can understand the desire for "out of the box" convenience.
> 
> 
> Hmmm.... given these two statements:
> 
> 1. char[] zig = Formatter("{0} {1}", "ciao", "bella");
> 2. char[] zag = Formatter("{0} {1}", "one", "two");
> 
> Questions:
> 
> A. If these are done sequentially, will zig be affected by the processing of 'zag'?  (I.e. because of buffer sharing.)

no


> B. Will doing 1 and 2 from different threads affect zig or zag?

no

> 
> If the answer to A and B is both "NO", then I have no problem with using Formatter.  I don't care about free function specifically (i.e. for getting a pointer or something), I just want safety, efficiency and clean syntax.
> 
> Documentation for Sprint suggests that both 1 and 2 are dangerous, I don't know if Formatter is like Sprint in that regard.

The doc says that each instance of Sprint should not be shared. Each thread can happily create it's own Sprint instance and use that. It's a nice solution when you're doing lots of fiddly formatting, or need to do some formatting for a logger, or whatnot. Once instantiated it doesn't hit the heap ... that's the only benefit. In fact, it's really just a thin wrapper around:

# Formatter.sprint (char[] output, char[] format, ...)

Another option for multi-threads is to synch on the Sprint object; but that's obviously somewhat less efficient.


> 
>>> 2. toString and toUtf8 (collisions)
>>>
>>> The change of the terminology is actually okay with me.
>>>
>>> But phobos has a way of using toString as both a method and a top-level function name, all over the place.  This gets really clumsy because you can never use the top level function names when writing a class unless you fully qualify them.
>>>
>>> For example, std.cpuid.toString(), always has to be fully qualified when called from a class, and seems nondescriptive anyway.  All the std.conv.toString() functions are nice but it's easy to accidentally call the in-class toString() by accident.
>>>
>>> For the utf8 <--> utf16 and similar, it's frustrating to have to do this:
>>>
>>> dchar[] x32 = ...;
>>> char[] x8 = tango.text.convert.Utf.toUtf8(x32);
>>>
>>> But you have to fully qualify if you are writing code in any class or struct.  If these were given another name, like makeUtf8, then these collisions would not happen.
>>
>>
>> One aspect of the Mango design that has carried forward into Tango is that similar functions are typically intended to live in their own namespace for the sake of clarity.  Previously, most/all of the free functions were declared in structs simply to prevent collisions, but this had code bloat issues so the design was changed.  Now, users are encouraged to use the aliasing import to produce the same effect:
>>
>> import Utf = tango.text.convert.Utf;
>>
>> Utf.toUtf8( x32 );
>>
>> I'll admit it's not as convenient as simply importing and using the functions, but it does make the origin of every function call quite clear.  I personally avoid "using" in C++ for exactly this reason--if I'm using an external routine I want to know what library it's from by inspection.
>>
>>
>> Sean
> 
> 
> This is not earth-shaking to me, so the current way is not a big deal, but what I want to avoid is what I think of as the Java naming effect, where you need to do this:
> 
> System.out.print(foo);
> 
> ... to print something.  To me, the design of a programming language or library is like a natural language.  In english we say "tin can" but we always say "can" when there is no ambiguity.  You never say "I want to buy a tin can of beans".  (I think that in the UK, they say "tin of beans" instead, but its the same idea.)

They do - and a lot of extra large tins are consumed :)


> 
> My view is for the common things to be simple and the complex things to be as simple as possible.  The extra formality of spelling out the full names of things is something that people find comfort in (*), but I would as soon do without in D.

That's a tough call, as you note below. We were discussing options on this today, so we'll see what evolves?


> 
> (*) I think people find comfort in it because they have been abused by other languages.  In C and C++ land, I agree --- if you do a '#define binary 1' in an include file somewhere, you can kill an algorithm in another file that is a dozen includes up the chain -- I found exactly this definition in a file at my job, and it was an 'interesting' problem to debug.  Working on large C and C++ projects breeds a kind of paranoia about symbol tables that I can completely relate to.
> 
> Sometimes the combination of #include and #define is a lot like "come from" in the way that it messes with the debugging process.
> 
> http://en.wikipedia.org/wiki/Come_from
> 
> But again, sorry if I'm being nit picky.

Not at all!

Tango is in early Beta, and this is exactly what's needed to file off the rough edges. We may not implement *everything* that everyone suggests, but every bug-report and every little nit-pick is wholly welcomed; seriously :)

>
> Kevin
> 
February 11, 2007
kris wrote:
> Kevin Bealer wrote:
>> Sean Kelly wrote:
>>
>>> Kevin Bealer wrote:
>>>
>>>>
>>>> O...
> 
> The doc says that each instance of Sprint should not be shared. Each thread can happily create it's own Sprint instance and use that. It's a nice solution when you're doing lots of fiddly formatting, or need to do some formatting for a logger, or whatnot. Once instantiated it doesn't hit the heap ... that's the only benefit. In fact, it's really just a thin wrapper around:
> 
> # Formatter.sprint (char[] output, char[] format, ...)
> 
> Another option for multi-threads is to synch on the Sprint object; but that's obviously somewhat less efficient.

Ouch.  That's ambiguous syntax.  Do you mean the programs shouldn't share the buffer, or that the library should ensure that sharing doesn't happen?  Either way fits the statement. I would have read it as meaning the first, but you seem to be saying that it's the second.
> 
> 
...
> Not at all!
> 
> Tango is in early Beta, and this is exactly what's needed to file off the rough edges. We may not implement *everything* that everyone suggests, but every bug-report and every little nit-pick is wholly welcomed; seriously :)
> 
>>
>> Kevin
>>
I'm not sure whether you were quoting the documentation, or reporting your understanding.  If you were quoting the documentation, I think it needs editing.

P.S.:  If I remember properly, in an earlier beta sharing the buffers in one's program resulted in run-time errors (buffer overwrites) that weren't detected...unless one wrote checks for the error.  This may have been a slightly different case, however.
February 11, 2007
Charles D Hixson wrote:
> I'm not sure whether you were quoting the documentation, or reporting your understanding.  If you were quoting the documentation, I think it needs editing.

From the doc: "Please note that the class itself is stateful, and therefore a single instance is not shareable across multiple threads."

Thus, it is considered unwise to share a single instance of Sprint across multiple threads. However, multiple threads /can/ share a single instance if they follow this pattern:

synchronized (GlobalSprint)
              GlobalSprint ("do my formatting", with, these, args);

This is a fairly standard mechanism in D for sharing resources across threads, and it's what I had referred to.

But there's no reason to do this kind of thing at all. The use-case for Sprint is to keep a handy formatter around for doing fast and convenient layout. Adding synchronized to the mix tends to defeat one of those desirable attributes, so we don't recommend it :)

If you're content to stash layout content into a temporary buffer instead, there's Formatter.sprint() which takes an output array. The output array in such a case would typically be stack-based.
February 11, 2007
kris wrote:
> Charles D Hixson wrote:
>> I'm not sure whether you were quoting the documentation, or reporting your understanding.  If you were quoting the documentation, I think it needs editing.
> 
>  From the doc: "Please note that the class itself is stateful, and therefore a single instance is not shareable across multiple threads."
> 
> Thus, it is considered unwise to share a single instance of Sprint across multiple threads. However, multiple threads /can/ share a single instance if they follow this pattern:
> 
> synchronized (GlobalSprint)
>               GlobalSprint ("do my formatting", with, these, args);
> 
> This is a fairly standard mechanism in D for sharing resources across threads, and it's what I had referred to.
> 
> But there's no reason to do this kind of thing at all. The use-case for Sprint is to keep a handy formatter around for doing fast and convenient layout. Adding synchronized to the mix tends to defeat one of those desirable attributes, so we don't recommend it :)
> 
> If you're content to stash layout content into a temporary buffer instead, there's Formatter.sprint() which takes an output array. The output array in such a case would typically be stack-based.
Mmmm...
The problem that I ran into with this before didn't involve multiple threads...but it did involve writing out to a file and to a console.  Unfortunately, It's been several months, so I don't remember the particulars.  The resolution, however, was to allocate multiple buffers within the same routine.  It might have involved buffering several different items within the same statement.  I think that may have been where things went wrong.  And my choices were to allocate several different buffers, or to break the statement into several different statements.

That sounds right, but don't depend on it.  As I said it's been several months.
February 11, 2007
Charles D Hixson wrote:
> kris wrote:
> 
>> Charles D Hixson wrote:
>>
>>> I'm not sure whether you were quoting the documentation, or reporting your understanding.  If you were quoting the documentation, I think it needs editing.
>>
>>
>>  From the doc: "Please note that the class itself is stateful, and therefore a single instance is not shareable across multiple threads."
>>
>> Thus, it is considered unwise to share a single instance of Sprint across multiple threads. However, multiple threads /can/ share a single instance if they follow this pattern:
>>
>> synchronized (GlobalSprint)
>>               GlobalSprint ("do my formatting", with, these, args);
>>
>> This is a fairly standard mechanism in D for sharing resources across threads, and it's what I had referred to.
>>
>> But there's no reason to do this kind of thing at all. The use-case for Sprint is to keep a handy formatter around for doing fast and convenient layout. Adding synchronized to the mix tends to defeat one of those desirable attributes, so we don't recommend it :)
>>
>> If you're content to stash layout content into a temporary buffer instead, there's Formatter.sprint() which takes an output array. The output array in such a case would typically be stack-based.
> 
> Mmmm...
> The problem that I ran into with this before didn't involve multiple threads...but it did involve writing out to a file and to a console.  Unfortunately, It's been several months, so I don't remember the particulars.  The resolution, however, was to allocate multiple buffers within the same routine.  It might have involved buffering several different items within the same statement.  I think that may have been where things went wrong.  And my choices were to allocate several different buffers, or to break the statement into several different statements.
> 
> That sounds right, but don't depend on it.  As I said it's been several months.

If you /retained/ a reference to the internal content, and then subsequently wrote over it via a second usage, then yes. You just witnessed a mutated alias at work. That's to be expected from any mutable shared state (multi-threaded or otherwise), unless you explicitly .dup the results before you stash them away (which then eliminates the alias).

Still, if this little wrapper is causing such difficulties, perhaps it should be removed instead. Embedding the .dup in the return value is certainly one alternative, but then that would defeat the overall intent also :)

- Kris
February 11, 2007
Frits van Bommel wrote:
> On a related note, one of the things that bothers /me/ is that no flush is performed at the end of the program. That causes some or all of the output to be missing if you don't explicitly flush after the last output.
> I'd suggest adding the following to tango.io.Console:
> ---
> static ~this ()
> {
>          Cout.flush();
>          Cerr.flush();
> }
> ---
> 
> That would fix it, I think.

I considered this, but I'll try to explain why I didn't suggest it. Basically, it hides that fact that you have forgotten to flush.

Pretend that this example is a big piece of code, that sometimes crashes.  To debug, you print some numbers, and where the numbers stop should be were the program crashes.

---
import tango.io.Stdout;
import tango.stdc.stdlib;
import tango.stdc.time;

void main()
{
	// some code here

	Stdout("1");

	// silly example of 'sometimes crash'
	if (time(null) & 1)
		free(cast(void*)1);
	
	Stdout("2");	

	// more code here, etc

	Stdout("3");
}
---

The problem is that with auto flush at program end, it will print '123' when it doesn't crash, and nothing at all when it does crash.  So the flushing happens at the 'wrong' time, unless you're aware of the inner workings of Tango.  A crash, exit by calling exit() or abort(), or an uncaught exception will cause the auto flush not to happen.  Which seems a bit inconsistent.

How realistic or valid this concern is, I'm not sure.  But it's at least it made me not suggest auto flushing.

By the way, is there a particular reason why Cerr is buffered?
February 11, 2007
Bill Baxter wrote:
> Is there any reason not to make the format item's index also optional? So that
>     Formatter("{} {} {}", a, b, c);
> can be used?  I mean making it more like %s?

I like it. :)