Semantics of toString

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Thu Nov 12 07:29:17 PST 2009


Steven Schveighoffer wrote:
> On Tue, 10 Nov 2009 18:49:54 -0500, Andrei Alexandrescu 
> <SeeWebsiteForEmail at erdani.org> wrote:
> 
>> I think the best option for toString is to take an output range and 
>> write to it. (The sink is a simplified range.)
> 
> Bad idea...
> 
> A range only makes sense as a struct, not an interface/object.  I'll 
> tell you why: performance.

You are right. If range interfaces accommodate block transfers, this 
problem may be addressed. I agree that one virtual call per character 
output would be overkill. (I seem to recall it's one of the reasons why 
C++'s iostreams are so inefficient.)

> Ranges are special in two respects:
> 
> 1. They are foreachable.  I think everyone agrees that calling 2 
> interface functions per loop iteration is much lower performing than 
> using opApply, which calls one delegate function per loop.  My 
> recommendation -- use opApply when dealing with polymorphism.  I don't 
> think there's a way around this.
 >
> 2. They are useful for passing to std.algorithm.  But std.algorithm is 
> template-interfaced.  No need for using interfaces because the correct 
> instatiation will be chosen.
> 
> If you are intending to add a streaming module that uses ranges, would 
> it not be templated for the range type as std.algorithm is?  If not, the 
> next logical choice is a delegate, which requires no vtable lookup.  
> Using an interface is just asking for a performance penalty for not much 
> gain.

I think the cost of calling through the delegate is roughly the same as 
a virtual call.

> Here's what I mean by not much gain: I would expect a stream range that 
> does output to have a method in it for outputting a buffer (I'd laugh at 
> you if you wanted to define a stream range that outputs a character at a 
> time).  So the difference between:

Well I'd laugh at you if you thought I'm that brain dead :o).

> x.toString(outputRange, format)
> 
> and
> 
> x.toString(&outputRange.sink, format)
> 
> is pretty darn minimal, and if outputRange is an interface or object, 
> this saves a virtual call per buffer write.  Plus the second form is 
> more universal, you can pass any delegate, and not have to use a range 
> type to wrap a delegate.
> 
> Don't fall into the "OOP newbie" trap -- where just because you've found 
> a new concept that is amazing, you want to use it for everything.  I say 
> this because I've seen in the past where someone discovers the power of 
> OOP and then wants to use it for everything, when in some cases, it's 
> overkill.  Just look at some Java "classes"...

There is no need to worry that I'll fall into at least that particular 
OOP newbie trap.

What I think we should do is define a text output interface that allows 
writing individual characters of all widths and also arrays of all 
widths. That would be a universal means for text output.

interface TextOutputStream {
     void put(dchar); // also accommodates char and wchar
     void put(in char[]);
     void put(in wchar[]);
     void put(in dchar[]);
}

The toString method (re-baptized as toStream) would take such an 
interface. Better ideas are always welcome. Perhaps I'm falling another 
OOP newbie trap! (Seriously!)

One possible course of action would be to extend the text output stream 
to print (and possibly format) some or all primitive types, a la today's 
phobos streams. That would make TextOutputStream fatter and more 
diluted, something that I don't like. But then we might define a 
FormattingTextOutputStream that extends TextOutputStream with all that 
stuff.

>  From another thread:
>> Walter does not feel strongly about Phobos.
> 
> Huh?  I feel like this sentence doesn't make sense, so maybe there's a 
> typo.

I meant to say, Walter does not want to do library design.


Andrei



More information about the Digitalmars-d mailing list