www.digitalmars.com         C & C++   DMDScript  

D - variable number of arguments

reply "EvilOne" <evilone omen.ru> writes:
Is there support for variable number of arguments in D? Something
like VB's ParamArray, or maybe another (better?) way... or is this
something that's not going to be the language feature?
Oct 04 2001
parent reply a <a b.c> writes:
EvilOne wrote:
 
 Is there support for variable number of arguments in D? Something
 like VB's ParamArray, or maybe another (better?) way... or is this
 something that's not going to be the language feature?
printf is supported. I would guess that something like C's varargs will be supported. It's a shame that useful (in my mind) features can be dropped because that are dangerous, but we keep C's varargs (and printf for that matter). (I guess this is assuming that varargs must be supported for printf) Doesn't var-args rely on macros? Dan
Oct 04 2001
parent reply "Walter" <walter digitalmars.com> writes:
a wrote in message <3BBD3952.29C9B2A4 b.c>...
EvilOne wrote:
 Is there support for variable number of arguments in D? Something
 like VB's ParamArray, or maybe another (better?) way... or is this
 something that's not going to be the language feature?
printf is supported. I would guess that something like C's varargs will be supported. It's a shame that useful (in my mind) features can be dropped because that are dangerous, but we keep C's varargs (and printf for that matter). (I guess this is assuming that varargs must be supported for printf) Doesn't var-args rely on macros?
D will support variable arguments, but only in interfacing to C functions. It's only real use is for printf(), etc.
Oct 05 2001
parent reply pontus <popi5501 student.uu.se> writes:
SGVsbG8NCg0KSW4gYSBnZW5lcmFsIHdheSwgY291bGRuJ3QgaXQgYmUgc29sdmVkIGxpa2Ug
dGhpczoNCg0Kdm9pZCBwcmludGYoImZvcm1hdCBzdHJpbmciLCAqdm9pZFtdKTsNCg0KVGhh
dCBpcyBhbiBhcnJheSB3aXRoIHBvaW50ZXJzKGhvcGUgaSBnb3QgdGhlIHN5bnRheCByaWdo
dCkuIEFuZCBzaW5jZSBhcnJheQ0KbGVuZ2h0cyBhcmUgYSBwcm9wZXJ0eSBvZiBhcnJheSB0
aGlzIHdvdWxkIGJlIGFuIGVhc3kgdGhpbmcgdG8gaW1wbGVtZW50Lg0KQSBvYnZpb3VzIHBy
b2JsZW0gdGhvdWdoLCBpcyBob3cgd291bGQgeW91IGtub3cgdGhhdCB0aGUgdGhpbmcgcG9p
bnRlZCB0byBhdA0KcGxhY2UgbiBpcyB0aGUgc2FtZSB0eXBlIGFzIGVudGl0eSBuIGluIHRo
ZSBmb3JtYXQgc3RyaW5nLCBidXQgZnJvbSB3aGF0IEkNCmNhbiBzZWUgdGhhdCBpcyBhIHBy
b2JsZW0gd2l0aCB2YXIgYXJncyB0by4NCg0KL1BvbnR1cw0K
Oct 07 2001
parent reply "Walter" <walter digitalmars.com> writes:
The problem is that arguments on the stack can be different sizes.

pontus wrote in message <3BC071D5.D574F0AC student.uu.se>...
Hello

In a general way, couldn't it be solved like this:

void printf("format string", *void[]);

That is an array with pointers(hope i got the syntax right). And since array
lenghts are a property of array this would be an easy thing to implement.
A obvious problem though, is how would you know that the thing pointed to at
place n is the same type as entity n in the format string, but from what I
can see that is a problem with var args to.

/Pontus
Oct 07 2001
parent reply Pontus Pihlgren <popi5501 student.uu.se> writes:
Well,maybe i'm missing something, but with a string and an array of
pointers, what on the stack have diffrent size?

Walter wrote:
 
 The problem is that arguments on the stack can be different sizes.
 
 pontus wrote in message <3BC071D5.D574F0AC student.uu.se>...
 Hello
 
 In a general way, couldn't it be solved like this:
 
 void printf("format string", *void[]);
 
 That is an array with pointers(hope i got the syntax right). And since array
 lenghts are a property of array this would be an easy thing to implement.
 A obvious problem though, is how would you know that the thing pointed to at
 place n is the same type as entity n in the format string, but from what I
 can see that is a problem with var args to.
 
 /Pontus
Oct 08 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
Pontus Pihlgren wrote:

void myfunc(char a, char b, char);

will possibly put something like this on stack:
+--+--+--+
|  a |  b  |  c |
+--+--+--+

where a, b and c is only one byte long.

Today on almost all 32bit processesor (void *) ist 4 byte long
so *void[3] will look like this
+-----------+-----------+-----------+
|    *void[0]         |    *void[1]         |    *void[2]        |
+-----------+-----------+-----------+

each element is 4 bytes wide, see the difference? and why (void *) can most 
times not be used to track function arguments. 

It is only possible if the compiler always puts 4 bytes on stack for every 
argument even smaller ones, and of course also never bigger ones like 'long 
long', but this might be profuseness, however honestly I've no idea how 
compilers do today... or how printf("%c%c%c", a, b, c); will be placed on 
stack for an x86 processsor, maybe it uses 4 byte frames after all, in 
which case a (void **) could actually function...
Oct 08 2001
parent reply "Walter" <walter digitalmars.com> writes:
Given:

    printf(" ... ", char, int, long long, double, long double);

the compiler pushes on the stack:
    char    4
    int    4
    long long    8
    double    8
    ld        12

and in general any struct can be passed by value, and can be any (aligned)
size. Every scheme I've come up with to deal with this is inefficient.
Oct 08 2001
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
What were your (Walter's) thoughts about the recursive scheme I cooked up?  I
remember other people commented on it, but I don't remember you speaking about
it.



D code:
    char[] printf(char[],....)  // declares that variable argument lists are
ok, no body may be defined
    out(result)
    {
        assert(result == "");
    };

    char[] printf(char[],char) {...};
    char[] printf(char[],int) {...};
    char[] printf(char[],char[]) {...};
    char[] printf(char[],void*) {...};

Each version of printf accepts as its first argument the format string.  It
will throw an exception if the format specifier is invalid for the type.
Otherwise, it does the printing and returns any remaining portion of the
format string.  The compiler expands the variable argument call inline:

D call:
  char c; char[] s; int i;
  printf("%c %s %d",c,s,i);

Expands to:
  char c; char[] s; int i;
  printf(printf(printf("%c %s %d",c),s),i);

None of which are variable argument fucntion calls, and all of which are
typesafe.  The innermost printf returns the string "%s %d", while the next
returns "%d", while the last returns "".  Only then is the out condition
checked; it is fine, so the program goes on.

Note that this requires that the 1st argument of the call be the same type as
the return type; anything else would be a syntax error in this scheme.  To
allow multiple fixed arguments, the format is similar:

int foo(int,  // recursive argument
          char,void*,char[], // these arguments are all called the same to all
copies of the recursion.
          ....);  // variable argument.

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Oct 09 2001
parent reply "EvilOne" <evilone omen.ru> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3BC2A336.74A74409 deming-os.org...
 What were your (Walter's) thoughts about the recursive scheme I cooked up?  I
 remember other people commented on it, but I don't remember you speaking about
 it.
[...] It would be even better if there's a standartized format string, so it could be generated by D compiler itself by just looking at argument list. So, you can type: char c; char[] s; printf(5, c, s); // format string will _automatically_ be "%d %c %s"
Oct 09 2001
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
EvilOne wrote:

 It would be even better if there's a standartized format string, so it
 could be generated by D compiler itself by just looking at argument list.
 So, you can type:

     char c; char[] s;
     printf(5, c, s);    // format string will _automatically_ be "%d %c %s"
But then, how would you get it to call printf("value of foo.bar.baz %d\n", foo.bar.baz); ? Anyhow, the compiler has all the information it needs to inline as much as it wants. It is perfectly conceivable that the compiler could inline the whole printf function, determine that there is a problem (and printf will throw an Exception) and so the compiler could throw a warning. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 09 2001
parent reply "EvilOne" <evilone omen.ru> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3BC31DC4.83500116 deming-os.org...
 EvilOne wrote:

 It would be even better if there's a standartized format string, so it
 could be generated by D compiler itself by just looking at argument list.
 So, you can type:

     char c; char[] s;
     printf(5, c, s);    // format string will _automatically_ be "%d %c %s"
But then, how would you get it to call printf("value of foo.bar.baz %d\n", foo.bar.baz);
printf("value of foo.bar.baz ", foo.bar.baz, "\n"); But of course, this is not printf(). So maybe call it print() or writeln()? Anyhow, I believe that such syntax is much clearer than of C-style printf(). Since there's no operator overloading in D, it's not possible to implement C++ iostreams with their intuitive usage of << and >>. But there must be a _simple_ way to perform at least console input and output. printf() is not the best choice here, imho.
Oct 10 2001
next sibling parent nik <no spam.net> writes:
Hello,

Please excuse me for intruding, but I too have been thinking along the
line of the EvilOne, that:

	printf(5, c, s, someObj);
should internally call:
	printf( 5.toString(), c.toString(), s.toString(), someObj.toString() );

where the toString() will provide the default formatting.

If one wants different formatting, than that object should override
the only other toString function from the base Object class:

	String toString(String fmt);

so now we can have:
	printf ( 5, c, s, someObj("-5x") ); // prefer this syntax
or
	printf ( 5, c, s, someObj.toString("-5x") ); // ... over this

I would rather have two functions print & println instead of printf, but
thats besides my point.

Also, to fix the vaarg problem, if the syntax
	{x, y, z}
creates a collection initialized with the objects x, y, z; then this
should work:
	println( { x, y ,z } );
or
	println( { "value of foo is", foo("-9.5f") } );

Anyway, just my 2c.

- nik.

EvilOne wrote:
 
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3BC31DC4.83500116 deming-os.org...
 EvilOne wrote:

 It would be even better if there's a standartized format string, so it
 could be generated by D compiler itself by just looking at argument list.
 So, you can type:

     char c; char[] s;
     printf(5, c, s);    // format string will _automatically_ be "%d %c %s"
But then, how would you get it to call printf("value of foo.bar.baz %d\n", foo.bar.baz);
printf("value of foo.bar.baz ", foo.bar.baz, "\n"); But of course, this is not printf(). So maybe call it print() or writeln()? Anyhow, I believe that such syntax is much clearer than of C-style printf(). Since there's no operator overloading in D, it's not possible to implement C++ iostreams with their intuitive usage of << and >>. But there must be a _simple_ way to perform at least console input and output. printf() is not the best choice here, imho.
Oct 10 2001
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
EvilOne wrote:

 printf("value of foo.bar.baz ", foo.bar.baz, "\n");

 But of course, this is not printf(). So maybe call it print() or writeln()?
 Anyhow, I believe that such syntax is much clearer than of C-style printf().
I see your point, and it's valid to think that it's clearer. I disagree :) For simplistic printing, cout has some real advantages. But for complex ones, it really gets weak: printf("value of my variable: %.8x\n", foo->GetOrthogonalBaz() ->Barrify() ->GetNormal(1,2,3) ->MakeHorizontal(false) ->GetX()); I would challenge anyone here to write the cout code that matches the formatting capabilities of printf. I would also challenge anybody to write cout code that expresses, as clearly as the printf format string, how the stuff will actually look on the screen. IMHO, printf shines where you need complex formatting and/or the arguments are complex.
 Since there's no operator overloading in D, it's not possible to implement
 C++ iostreams with their intuitive usage of << and >>. But there must be
 a _simple_ way to perform at least console input and output. printf() is
 not the best choice here, imho.
Fair enough. I like << and >> primarily because of their type-safety (scanf is HORRID). However, there is a lot of elegance to the code: int i; cin >> i; Which is more visual than int i; scanf("%d",&i); Or, what I normally end up doing because I can never remember how scanf works: int i; char buf[32]; fgets(buf,32,stdin); i = atoi(buf); -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 10 2001
next sibling parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 EvilOne wrote:
 
 printf("value of foo.bar.baz ", foo.bar.baz, "\n");

 But of course, this is not printf(). So maybe call it print() or writeln()?
 Anyhow, I believe that such syntax is much clearer than of C-style printf().
I see your point, and it's valid to think that it's clearer. I disagree :) For simplistic printing, cout has some real advantages. But for complex ones, it really gets weak: printf("value of my variable: %.8x\n", foo->GetOrthogonalBaz() ->Barrify() ->GetNormal(1,2,3) ->MakeHorizontal(false) ->GetX()); I would challenge anyone here to write the cout code that matches the formatting capabilities of printf.
cout << "value of my variable: " << setw(8) << hex << foo->GetOrthogonalBaz() ->Barrify() ->GetNormal(1,2,3) ->MakeHorizontal(false) ->GetX() << endl;
 I
 would also challenge anybody to write cout code that expresses, as clearly as
the printf format string, how
 the stuff will actually look on the screen.
I don't think the above is all that bad really. As far as clearness, I've never though all the %magic was really that clear. It's ugly as sin to tell the truth. Of course if you want things to appear in the code as they would on the screen, we could go back to COBOL picture statements. Perl has a similar feature that they are about to rip out of the perl core. I'm not against having something like printf in D, but I have two issues. It is real nice to be able to define input and output routines for user defined types that can be used with the standard I/O code, since some user types are simply made to make up for the deficiencies in the language's built-ins. (Dates, vectors, etc.) The other is that printf can be a huge security hole. You may challenge people write cout code that you think is pretty. I challenge anyone to implement a printf that isn't capable of producing security breaches. (I'm looking at the format string when I say that.)
 IMHO, printf shines where you need complex formatting and/or the arguments are
complex.
And the beauty of iostreams is that you can write code so that even the most complex types can be read or written as easily as a simple infix expression.
 Fair enough.  I like << and >> primarily because of their type-safety (scanf
is HORRID).  However, there is a
 lot of elegance to the code:
 
 int i;
 cin >> i;
 
 Which is more visual than
 
 int i;
 scanf("%d",&i);
 
 Or, what I normally end up doing because I can never remember how scanf works:
 
 int i;
 char buf[32];
 fgets(buf,32,stdin);
 i = atoi(buf);
scanf is horrid, but printf isn't? They are the left and right arms of the same depraved beast. Dan
Oct 10 2001
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 printf("value of my variable: %.8x\n",
         foo->GetOrthogonalBaz()
                ->Barrify()
                ->GetNormal(1,2,3)
                ->MakeHorizontal(false)
                ->GetX());

 I would challenge anyone here to write the cout code that matches the
formatting capabilities of printf.
cout << "value of my variable: " << setw(8) << hex << foo->GetOrthogonalBaz() ->Barrify() ->GetNormal(1,2,3) ->MakeHorizontal(false) ->GetX() << endl;
clap,clap,clap. I couldn't have done that. One question...doesn't the "hex" format persist? Shouldn't you reset that somehow, so that it doesn't corrupt later print on cout? Or does it reset after printing one thing?
 I
 would also challenge anybody to write cout code that expresses, as clearly as
the printf format string, how
 the stuff will actually look on the screen.
I don't think the above is all that bad really. As far as clearness, I've never though all the %magic was really that clear. It's ugly as sin to tell the truth. Of course if you want things to appear in the code as they would on the screen, we could go back to COBOL picture statements. Perl has a similar feature that they are about to rip out of the perl core.
True, %magic is ugly, but I haven't seen better yet. I don't know anything about COBOL, so I can't really speak to it...
         I'm not against having something like printf in D, but I have two
 issues.  It is real nice to be able to define input and output routines
 for user defined types that can be used with the standard I/O code,
 since some user types are simply made to make up for the deficiencies in
 the language's built-ins.  (Dates, vectors, etc.)
True, this is nice. Maybe functions of the base class ('object', I think it was) that you can override.
 The other is that
 printf can be a huge security hole.  You may challenge people write cout
 code that you think is pretty.  I challenge anyone to implement a printf
 that isn't capable of producing security breaches.  (I'm looking at the
 format string when I say that.)
I've heard of such things...things like the ability to read memory directly and such. Can you give examples of it, since I haven't seen it firsthand? I thought that printf in general...where one %magic correlates to one argument, and there are none of the special specifires...was secure, even if all of the extensions of it are not. Am I wrong? (p.s. I know that sprintf can cause buffer overflows...but other than that...)
 scanf is horrid, but printf isn't?  They are the left and right arms of
 the same depraved beast.
I don't think that that's fair. To me, it seems that printf was designed to allow flexible printing syntax. As long as you don't mix the wrong formatter with the wrong type, you're ok...and were looking to see if there's a way to make a D version of printf that is typesafe so you can't use the wrong formatter. It appears to me that scanf was designed to look like printf...but in doing so, it created a nightmare. scanf should have been designed very differently, in a way that made sense for reading, just like printf was designed to work well with output. Anyhow, this is all just my opinion. I use both cout and printf as my whims dictate :) -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 10 2001
next sibling parent "EvilOne" <evilone omen.ru> writes:
 Anyhow, this is all just my opinion.  I use both cout and printf as my whims
dictate :)
The question is, what we'll have in D?
Oct 10 2001
prev sibling parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 a wrote:
         cout << "value of my variable: "
              << setw(8) << hex
              << foo->GetOrthogonalBaz()
                         ->Barrify()
                         ->GetNormal(1,2,3)
                         ->MakeHorizontal(false)
                         ->GetX()
              << endl;
clap,clap,clap. I couldn't have done that. One question...doesn't the "hex" format persist? Shouldn't you reset that somehow, so that it doesn't corrupt later print on cout? Or does it reset after printing one thing?
You got me. I tried to convince myself that hex worked like setw() and a number of the other one shot manipulators. It kind of sucks in this case. You would have to save the state flag before the cout and restore it when you're done doing hex.
 True, %magic is ugly, but I haven't seen better yet.  I don't know anything
about COBOL, so I can't really speak to
 it...
Oh, you missed out. You define a format line that specifies who field will be laid out. Then when you print you give it the format name a a list variables (a record actually that you've populated with the data).
         I'm not against having something like printf in D, but I have two
 issues.  It is real nice to be able to define input and output routines
 for user defined types that can be used with the standard I/O code,
 since some user types are simply made to make up for the deficiencies in
 the language's built-ins.  (Dates, vectors, etc.)
True, this is nice. Maybe functions of the base class ('object', I think it was) that you can override.
You still need to create your own %magic symbol to use it with printf, so he knows how to handle the argument. That just make the % problem worse, if not open ended. I admit that printf does what it does better that most anything else. It's just not capable of being expanded in a reasonable way.
 The other is that
 printf can be a huge security hole.  You may challenge people write cout
 code that you think is pretty.  I challenge anyone to implement a printf
 that isn't capable of producing security breaches.  (I'm looking at the
 format string when I say that.)
I've heard of such things...things like the ability to read memory directly and such. Can you give examples of it, since I haven't seen it firsthand?
Not on short notice. :-)
 I thought that printf in general...where one %magic correlates to one
argument, and there are none of the special
 specifires...was secure, even if all of the extensions of it are not.  Am I
wrong?  (p.s. I know that sprintf can
 cause buffer overflows...but other than that...)
 
 scanf is horrid, but printf isn't?  They are the left and right arms of
 the same depraved beast.
I don't think that that's fair.
Well I am trying to vilify stdio after all...
To me, it seems that printf was designed to allow flexible printing syntax.  As
 long as you don't mix the wrong formatter with the wrong type, you're ok...and
were looking to see if there's a way
 to make a D version of printf that is typesafe so you can't use the wrong
formatter.
If it's type safe, then you don't need the formatter in most cases. The %i, %d, %s, %c, %x, etc. formats are redundant if printf could be made type safe. They would allow you to put an implicit case of some sort in the format string, but that sounds nasty to me. And it defies the "Only one way to possibly do it." ideology that D seems to be going for. (Maybe I do like it.) Maybe we could define a function that takes a list of class printable objects (and class Object could have a cast defined that users could override) and then we could allow optional modifiers to be applied to the arguments. There is no point in forcing some to specify formats when all he wants say is: print("It was: ", it, "\n");
 It appears to me that scanf was designed to look like printf...but in doing
so, it created a nightmare.  scanf
 should have been designed very differently, in a way that made sense for
reading, just like printf was designed to
 work well with output.
printf allows aggregate printing. scanf allow aggregate reading. I used to work with folks who loved scanf the way you love printf. Both are better than the world of have a read and print function for every data type imaginable for the user to memorize the options to. It's just that it feels like it is time to get fed up and improve things again.
 Anyhow, this is all just my opinion.  I use both cout and printf as my whims
dictate :)
Same here. Recently I began to force myself to use cout even when it hurts so I can learn how to criticize it more meaningfully. It ain't printf but it's not as bad as most people make it out to be. It's cool that you can make your own custom manipulators easily, but it would be nicer if you could define a manipulator type and assign it the OR/accumulation/whatever a bunch of manipulations together so you could then apply it to a variable being inserted. form F = hex + width(5) + center; cout << x << F + y << width(3) + right + z << endl; It still needs work, but it give the basic idea. I just used plus every where because I was at a loss for words. The accumulation operator should be different from the apply operator. Maybe it could be spelled: form F = hex & width(5) & center; cout << x << F|y << width(3)&right|z << end; It might be nice to be apply to apply to a list of arguments too. But I digress. This type of syntax won't work for D. Dan
Oct 10 2001
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

         I'm not against having something like printf in D, but I have two
 issues.  It is real nice to be able to define input and output routines
 for user defined types that can be used with the standard I/O code,
 since some user types are simply made to make up for the deficiencies in
 the language's built-ins.  (Dates, vectors, etc.)
True, this is nice. Maybe functions of the base class ('object', I think it was) that you can override.
You still need to create your own %magic symbol to use it with printf, so he knows how to handle the argument. That just make the % problem worse, if not open ended. I admit that printf does what it does better that most anything else. It's just not capable of being expanded in a reasonable way.
How about: class object // base for everything else { public: char[] toString() { // default value returns a hex print of the 'this' pointer }; }; The implementation recognizes a new %magic that is only legal when matched to an argument that is a pointer to object (or a child, ofc). Any classes that want to override how they are printed must override toString(). Thus, you only add 1 %magic that will work with all classes. It doesn't help with structs, ofc...
 scanf is horrid, but printf isn't?  They are the left and right arms of
 the same depraved beast.
I don't think that that's fair.
Well I am trying to vilify stdio after all...
LOL
         If it's type safe, then you don't need the formatter in most cases.
 The %i, %d, %s, %c, %x, etc. formats are redundant if printf could be
 made type safe.  They would allow you to put an implicit case of some
 sort in the format string, but that sounds nasty to me.  And it defies
 the "Only one way to possibly do it." ideology that D seems to be going
 for.  (Maybe I do like it.)
Hold on here...the point of those specifiers is to tell you how to print something. %d (and %i, which I think is identical) prints an integer in decimal format. %c prints the ASCII character that has the ASCII code given. %x and %X are two distinct ways of printing an integer as a hexdecimal value. All of these specifiers can work on the SAME argument! printf("%d %c %x %X",10,10,10,10); prints out "10 \n a A" The typesafety comes in when you try to pass a char[] and print it as an integer, or an integer and you try to print it as a string (meaning printf expects it to be a pointer value). In either of those cases, printf() would throw an exception.
 It appears to me that scanf was designed to look like printf...but in doing
so, it created a nightmare.  scanf
 should have been designed very differently, in a way that made sense for
reading, just like printf was designed to
 work well with output.
printf allows aggregate printing. scanf allow aggregate reading. I used to work with folks who loved scanf the way you love printf. Both are better than the world of have a read and print function for every data type imaginable for the user to memorize the options to. It's just that it feels like it is time to get fed up and improve things again.
I suppose scanf might work to my liking, if it was typesafe, wouldn't overrun buffers, and didn't require you to take the address of every little integer you're reading. I agree, it's time for an upgrade. cout might be a step in the right direction, but I think it's more of a step sideways. Let's brainstorm... :)
 Anyhow, this is all just my opinion.  I use both cout and printf as my whims
dictate :)
Same here. Recently I began to force myself to use cout even when it hurts so I can learn how to criticize it more meaningfully. It ain't printf but it's not as bad as most people make it out to be. It's cool that you can make your own custom manipulators easily, but it would be nicer if you could define a manipulator type and assign it the OR/accumulation/whatever a bunch of manipulations together so you could then apply it to a variable being inserted. form F = hex + width(5) + center; cout << x << F + y << width(3) + right + z << endl; It still needs work, but it give the basic idea. I just used plus every where because I was at a loss for words. The accumulation operator should be different from the apply operator. Maybe it could be spelled: form F = hex & width(5) & center; cout << x << F|y << width(3)&right|z << end; It might be nice to be apply to apply to a list of arguments too. But I digress. This type of syntax won't work for D.
I actually kind of like your syntax, personally. It has the potential for the compactness I like of printf with the all of the OOP benefits. And it's actually not as far from D syntax as you might think. What if we make some definitions: class FormElement; typedef FormElement[] form; form hex = { hexElement }; form width(int i) { return ... }; form center = { centerElement }; To accumulate types, you do this: form F = hex + width(5) + center; which D interprets as concatenating arrays. Now walter helps us by adding a property function called "format" which takes a form argument and returns a char[]. We also implement it as a function in 'object', so now we can print easily: stdout.print(x.format() + "this is a string" + y.format(F) + z.format(width(3)+right) + endl); Thoughts? -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 10 2001
next sibling parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 a wrote:
 You still need to create your own %magic symbol to use it with printf,
 so he knows how to handle the argument.  That just make the % problem
 worse, if not open ended.  I admit that printf does what it does better
 that most anything else.  It's just not capable of being expanded in a
 reasonable way.
How about: class object // base for everything else { public: char[] toString() { // default value returns a hex print of the 'this' pointer }; }; The implementation recognizes a new %magic that is only legal when matched to an argument that is a pointer to object (or a child, ofc). Any classes that want to override how they are printed must override toString(). Thus, you only add 1 %magic that will work with all classes. It doesn't help with structs, ofc...
Well, at the very least, I'd like to have a toStream method that can default to toString. The human readable format is not always the best/most complete way to store to a file. Given only one %magic symbol that can handle all descendants of Object would work, but you would only have one way of formatting, and how would you deal with the %modifiers? (Assuming we really want the kludge up printf this way.)
         If it's type safe, then you don't need the formatter in most cases.
 The %i, %d, %s, %c, %x, etc. formats are redundant if printf could be
 made type safe.  They would allow you to put an implicit case of some
 sort in the format string, but that sounds nasty to me.  And it defies
 the "Only one way to possibly do it." ideology that D seems to be going
 for.  (Maybe I do like it.)
Hold on here...the point of those specifiers is to tell you how to print something. %d (and %i, which I think is identical) prints an integer in decimal format. %c prints the ASCII character that has the ASCII code given. %x and %X are two distinct ways of printing an integer as a hexdecimal value. All of these specifiers can work on the SAME argument! printf("%d %c %x %X",10,10,10,10); prints out "10 \n a A" The typesafety comes in when you try to pass a char[] and print it as an integer, or an integer and you try to print it as a string (meaning printf expects it to be a pointer value). In either of those cases, printf() would throw an exception.
You are right that there is a difference between %d and %x (etc.) that we'd need to keep. In the above discussion, we could not have such differences in representation for objects. A pity. I will argue that you don't need %c or similar %magic that specify a type, but not a representation. In your above example, if you want 10 to be a char, cast it. that is the implicit cast I was talking about.
 It appears to me that scanf was designed to look like printf...but in doing
so, it created a nightmare.  scanf
 should have been designed very differently, in a way that made sense for
reading, just like printf was designed to
 work well with output.
printf allows aggregate printing. scanf allow aggregate reading. I used to work with folks who loved scanf the way you love printf. Both are better than the world of have a read and print function for every data type imaginable for the user to memorize the options to. It's just that it feels like it is time to get fed up and improve things again.
I suppose scanf might work to my liking, if it was typesafe, wouldn't overrun buffers, and didn't require you to take the address of every little integer you're reading. I agree, it's time for an upgrade. cout might be a step in the right direction, but I think it's more of a step sideways. Let's brainstorm... :)
The whole OO and infix think I think was in at least a vaguely forward direction.
         Same here.  Recently I began to force myself to use cout even when it
 hurts so I can learn how to criticize it more meaningfully.  It ain't
 printf but it's not as bad as most people make it out to be.  It's cool
 that you can make your own custom manipulators easily, but it would be
 nicer if you could define a manipulator type and assign it the
 OR/accumulation/whatever a bunch of manipulations together so you could
 then apply it to a variable being inserted.

         form F = hex + width(5) + center;
         cout << x << F + y << width(3) + right + z << endl;

 It still needs work, but it give the basic idea.  I just used plus every
 where because I was at a loss for words.  The accumulation operator
 should be different from the apply operator.  Maybe it could be spelled:

         form F = hex & width(5) & center;
         cout << x << F|y << width(3)&right|z << end;

 It might be nice to be apply to apply to a list of arguments too. But I
 digress.  This type of syntax won't work for D.
I actually kind of like your syntax, personally. It has the potential for the compactness I like of printf with the all of the OOP benefits. And it's actually not as far from D syntax as you might think. What if we make some definitions: class FormElement; typedef FormElement[] form; form hex = { hexElement }; form width(int i) { return ... }; form center = { centerElement }; To accumulate types, you do this: form F = hex + width(5) + center; which D interprets as concatenating arrays. Now walter helps us by adding a property function called "format" which takes a form argument and returns a char[]. We also implement it as a function in 'object', so now we can print easily: stdout.print(x.format() + "this is a string" + y.format(F) + z.format(width(3)+right) + endl); Thoughts?
Is the array thing really the best way to do this? I looks to me like a hack to get around the lack of operator overloads. It might feel better like: format.apply(hex).apply(right).apply(width(3)); I'm not crazy in love with this either. On an aesthetic level, I think I'm more accustomed to the format coming before the data being formatted. That's minor though. If enough people agree though, we could be the format something that take an argument and returns something streamable. I can say that I don't like the concatenations prior to the print operation. If print could safely do variable arguments, it could print/write them all without having to allocate an intermediate buffer the size of everything being printed and copying it. There would be excess time overhead with the extra copying and memory overhead with the temp string. This isn't a problem with print so much as it's the only way we can pass a variable number of objects to a print routine. Another aesthetic note. Say what you will about cout's failings, I do find the expression based syntax more visually pleasing. It's a style thing I think. I hate doing multi-line method invocations. Multi-line expressions don't for some reason unless it's an argument to function or control structure. In your example I would probably want to do: stdout.print(x.format() + "this is a string" + y.format(F) + z.format(width(3)+right) + endl); For some reason I like it better when it's not a method call. Cout's formatting is still gross in form and function but I find its code formats better for my eyes. I like the example less since those are pluses and not commas. For plain function calls, I don't tend to mind splitting lines on commas. Anything else just feels icky. I know my reasoning here is dippy, but style is like that. I think it would be worth brain storming out how the I/O might work. The same is true of other library like functions. Java got big before it's library was stable. I'm not sure I'd feel safe calling it stable yet. I'd hate to see D follow in Java's foot steps in this way. I've found the difference between versions of java to bother me more than the differences implementations of C on different platforms. It rivals C++. (Granted, I've had the supreme pleasure of not having to code for Windows.) Dan
Oct 13 2001
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 Russ Lewis wrote:
 a wrote:
 You still need to create your own %magic symbol to use it with printf,
 so he knows how to handle the argument.  That just make the % problem
 worse, if not open ended.  I admit that printf does what it does better
 that most anything else.  It's just not capable of being expanded in a
 reasonable way.
How about: class object // base for everything else { public: char[] toString() { // default value returns a hex print of the 'this' pointer }; }; The implementation recognizes a new %magic that is only legal when matched to an argument that is a pointer to object (or a child, ofc). Any classes that want to override how they are printed must override toString(). Thus, you only add 1 %magic that will work with all classes. It doesn't help with structs, ofc...
Well, at the very least, I'd like to have a toStream method that can default to toString. The human readable format is not always the best/most complete way to store to a file. Given only one %magic symbol that can handle all descendants of Object would work, but you would only have one way of formatting, and how would you deal with the %modifiers? (Assuming we really want the kludge up printf this way.)
What if toString had a char[] argument: stdout.print("%z %'asdf'z", obj1, obj2); The 'asdf' string would be passed to the toString call on obj2.
         I will argue that you don't need %c or similar %magic that specify a
 type, but not a representation.  In your above example, if you want 10
 to be a char, cast it.  that is the implicit cast I was talking about.
That's true, kind of. But there is a substantial difference between "print the decimal representation of this byte", "print the hexadecimal representation of this byte", and "print the ASCII representation of this byte", all of which are valid for a char type.
         Is the array thing really the best way to do this?
I certainly hope not!
 I looks to me like
 a hack to get around the lack of operator overloads.
Very true. It is very ugly; it was just a brainstorm to get things started. :)
 It might feel
 better like:
         format.apply(hex).apply(right).apply(width(3));
 I'm not crazy in love with this either.

         On an aesthetic level, I think I'm more accustomed to the format coming
 before the data being formatted.  That's minor though. If enough people
 agree though, we could be the format something that take an argument and
 returns something streamable.
char[] format(form,int) ??? -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 13 2001
parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 a wrote:
 
 Russ Lewis wrote:
 a wrote:
 You still need to create your own %magic symbol to use it with printf,
 so he knows how to handle the argument.  That just make the % problem
 worse, if not open ended.  I admit that printf does what it does better
 that most anything else.  It's just not capable of being expanded in a
 reasonable way.
How about: class object // base for everything else { public: char[] toString() { // default value returns a hex print of the 'this' pointer }; }; The implementation recognizes a new %magic that is only legal when matched to an argument that is a pointer to object (or a child, ofc). Any classes that want to override how they are printed must override toString(). Thus, you only add 1 %magic that will work with all classes. It doesn't help with structs, ofc...
Well, at the very least, I'd like to have a toStream method that can default to toString. The human readable format is not always the best/most complete way to store to a file. Given only one %magic symbol that can handle all descendants of Object would work, but you would only have one way of formatting, and how would you deal with the %modifiers? (Assuming we really want the kludge up printf this way.)
What if toString had a char[] argument: stdout.print("%z %'asdf'z", obj1, obj2); The 'asdf' string would be passed to the toString call on obj2.
I like this idea. I still don't like %magic, but this improves it a bit. It did just strike me though, we were going after typesafety somewhere back there. By have one %magic for all object, haven't we killed real typesafety? For instance, suppose you passed the wrong object type to your print statement above. It would be valid still. It may barf at runtime because it doesn't handle 'asdf'. Not to derail you, its a good idea, but how do we get typesafety back into it?
         I will argue that you don't need %c or similar %magic that specify a
 type, but not a representation.  In your above example, if you want 10
 to be a char, cast it.  that is the implicit cast I was talking about.
That's true, kind of. But there is a substantial difference between "print the decimal representation of this byte", "print the hexadecimal representation of this byte", and "print the ASCII representation of this byte", all of which are valid for a char type.
There a different representations, like hex, oct, dec, left, right, etc. but doesn't %h only work for type char because printf uses varargs converting chars into ints? I'm just used to the bare %magic being used to specify type. That notion should die and be replaced with type safety. Representation descriptors would still be valid, but in many cases a sane default should be acceptable. Make character type look like characters, make numbers look like numbers, etc.
         Is the array thing really the best way to do this?
I certainly hope not!
Sorry. :-)
 I looks to me like
 a hack to get around the lack of operator overloads.
Very true. It is very ugly; it was just a brainstorm to get things started. :)
Well, I guess the very first thing we ought to decide what type of syntax we want. If we go all procedural, we are ok. You said you would need a format property in your idea. I think I like that, but I'm not sure how to best leverage it. In any case that is something we would have to be Walter for. Likewise, if we want more on an expression based syntax, we either have to kludge around with the built-in types in D or we have to beg Walter to add a couple more primitives, with operators. At that point I'd say we are work in the language itself though and not in the library.
 It might feel
 better like:
         format.apply(hex).apply(right).apply(width(3));
 I'm not crazy in love with this either.

         On an aesthetic level, I think I'm more accustomed to the format coming
 before the data being formatted.  That's minor though. If enough people
 agree though, we could be the format something that take an argument and
 returns something streamable.
char[] format(form,int) ???
I'm not sure really. I guess I was thinking: form F = <manipulator(s)>; // custom stdio.print(v1, hex(v2), F(v3)); // default form, hex form, my custom form But think makes it difficult or applets nasty looking to apply multiple format manipulator to a single variable in the print statement. stdio.print(hex(width(3, right(v1)))); // lisp anyone I don't think I would want to have to declare a format type for every custom manipulation. It would give us one of the worst drawbacks of COBOL's and perl's record based output without the benefits. I'm also having trouble being objective coming up with something looks like it fits D's ideology. I'm real partial to C++'s i/o and in D that's right out. I like the suggestion you had above for %magic on object types but I don't think it goes far enough. Would help to work out a formalized set of string ops to build upon? Most I/O will either be binary records or text strings I would assume. The binary output wouldn't need much in the way of formatting. fixed record sizes, byte and bit ordering and that sort of thing should do. It's with the human readable that we worry and justification, representation, padding, text formation of complex types. Maybe we would have a better feel for what feels right for printing strings in D once we get a feel for what it would be like to just muck around with strings in D. Dan
Oct 13 2001
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

         I like this idea.  I still don't like %magic, but this improves it a
 bit.  It did just strike me though, we were going after typesafety
 somewhere back there.  By have one %magic for all object, haven't we
 killed real typesafety?  For instance, suppose you passed the wrong
 object type to your print statement above.  It would be valid still.  It
 may barf at runtime because it doesn't handle 'asdf'.  Not to derail
 you, its a good idea, but how do we get typesafety back into it?
The key with strong typing is that you know what you know and you don't have to make wild guesses or assumptions. When the %z (or whatever) magic is passed, if the typesafe printf sees that the matching argument is anything but a pointer to an object, then it throws an exception. If it is an object, then it calls toString with the (possibly zero length) char[] array that is the %z format qualifier. This call goes right to the overloaded version of this function in the class (remember, the compiler is smart enough to make all functions virtual). This class is then responsible for dealing with the qualifier. If your class doesn't deal with qualifiers, then you throw the exception if it is nonzero length. If you do, then you throw an exception if you don't recognize the format. Etc... Please clarify if I'm missing something, but this seems 100% typesafe to me - provided that the underlying printf varargs architecture is also typesafe.
 There a different representations, like hex, oct, dec, left, right, etc.
 but doesn't %h only work for type char because printf uses varargs
 converting chars into ints?  I'm just used to the bare %magic being used
 to specify type.  That notion should die and be replaced with type
 safety.  Representation descriptors would still be valid, but in many
 cases a sane default should be acceptable.  Make character type look
 like characters, make numbers look like numbers, etc.
Ah, yes. I see what you're saying now. And I totally agree. One of my big gripes with %x/%X is that if the high bit is 1, then we always get a 8-character printf string: printf("%x",(signed char)-1) prints "FFFFFFFF" whereas I wish it would print "FF". The hack around that that I found was to convert my arg to an UNsigned char...so that when it's promoted to a long, it is filled with 0's. Then printf works as I would like. In D's printf, passing a char should make the format default to only printing 2 characters (or less).
 I looks to me like
 a hack to get around the lack of operator overloads.
Very true. It is very ugly; it was just a brainstorm to get things started. :)
Well, I guess the very first thing we ought to decide what type of syntax we want. If we go all procedural, we are ok. You said you would need a format property in your idea. I think I like that, but I'm not sure how to best leverage it. In any case that is something we would have to be Walter for. Likewise, if we want more on an expression based syntax, we either have to kludge around with the built-in types in D or we have to beg Walter to add a couple more primitives, with operators. At that point I'd say we are work in the language itself though and not in the library.
We could go with the Perl syntax here (shudder). Use the dot operator to concatenate (formatted) strings. Any time that the compiler sees something of the form: char[] . foo It converts it internally to char[] + format(foo) where format(...) is a series of library routines that convert various things into char[]. The format(object) just calls toString() to do it. But now we have the problem of how to do format Ofc, this all requires that Walter implement some special case code for char[]...but one of the stated objectives of D was "Languages should be good at handling strings." Another thought. Instead of only making this work for char[], you could make it work for all array types. Earlier, in a discussion about casts, somebody talked about actually passing a type (not a value) as a parameter into a cast function: int i = cast(int,foo); We could do a similar thing with the . operator on arrays. char[] str = "" . foo . bar . baz; Is expanded to: char[] str = "" + format(char[],foo) + format(char[],bar) + format(char[],baz); I thought about using the cast syntax, but that becomes cloudy when the argument you're trying to format is a pointer or another array.
         I'm also having trouble being objective coming up with something looks
 like it fits D's ideology.  I'm real partial to C++'s i/o and in D
 that's right out.  I like the suggestion you had above for %magic on
 object types but I don't think it goes far enough.
I Walter doesn't mind the overloaded . operator idea, you could use << and >> instead: char[] str = "" . foo; becomes char[] str = "" + format(char[],foo); and str >> foo; becomes foo = extract(typeof(foo),str); Or something like that. Still no (good) solution for format specifiers. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 14 2001
parent reply a <a b.c> writes:
Russ Lewis wrote:

 The key with strong typing is that you know what you know and you don't have
to make wild guesses or assumptions.  When the %z (or
 whatever) magic is passed, if the typesafe printf sees that the matching
argument is anything but a pointer to an object, then it
 throws an exception.  If it is an object, then it calls toString with the
(possibly zero length) char[] array that is the %z format
 qualifier.  This call goes right to the overloaded version of this function in
the class (remember, the compiler is smart enough to
 make all functions virtual).  This class is then responsible for dealing with
the qualifier.
 
 If your class doesn't deal with qualifiers, then you throw the exception if it
is nonzero length.  If you do, then you throw an
 exception if you don't recognize the format.  Etc...
 
 Please clarify if I'm missing something, but this seems 100% typesafe to me -
provided that the underlying printf varargs
 architecture is also typesafe.
Well are typesafe in that it will always be a descendant of Object. But you can't be more specific. (I don't mean just any Fruit. I want an Apple!) You will always get the streamify routine for the Object you pass, but I guess I would like to say what type of Object I expect and get en error otherwise, like you would with builtin types. Not a show stopper by any means.
 There a different representations, like hex, oct, dec, left, right, etc.
 but doesn't %h only work for type char because printf uses varargs
 converting chars into ints?  I'm just used to the bare %magic being used
 to specify type.  That notion should die and be replaced with type
 safety.  Representation descriptors would still be valid, but in many
 cases a sane default should be acceptable.  Make character type look
 like characters, make numbers look like numbers, etc.
Ah, yes. I see what you're saying now. And I totally agree. One of my big gripes with %x/%X is that if the high bit is 1, then we always get a 8-character printf string: printf("%x",(signed char)-1) prints "FFFFFFFF" whereas I wish it would print "FF". The hack around that that I found was to convert my arg to an UNsigned char...so that when it's promoted to a long, it is filled with 0's. Then printf works as I would like. In D's printf, passing a char should make the format default to only printing 2 characters (or less).
Well, I guess I would rather that D would always print a char as a char/glyph, unless I cast it to something else (like an unsigned) in which case I would like to to print way ever I passed it. I do agree that in some cases there is format information that is useful and not associated to the type (unless we attach a format property; probably overkill) like hex/oct/dec, left/right/center, and the like, but I don't think I like the use of the format character as an end run around a cast. I C you didn't have a choice. printf only saw ints.
 We could go with the Perl syntax here (shudder).  Use the dot operator to
concatenate (formatted) strings.  Any time that the
 compiler sees something of the form:
 
 char[] . foo
 
 It converts it internally to
 
 char[] + format(foo)
 
 where format(...) is a series of library routines that convert various things
into char[].  The format(object) just calls
 toString() to do it.  But now we have the problem of how to do format

Actually, this isn't how perl does formatting. Your choices in perl are (s)printf and format lines like: format FOO price: $<<<<< tax: $>>>> $thePrice, $theTax to say the first value will be left justified and the second will be right justified. (I have the syntax wrong, but it's the general idea. You then have to tell the interpreter which format you are using for a given filehandle and then you do a write like so: ($thePrice, $theTax) = ($something, $somethingelse); write myFileHandle;
 Ofc, this all requires that Walter implement some special case code for
char[]...but one of the stated objectives of D was
 "Languages should be good at handling strings."
Oh, for string handling. I was thinking of something comparable to C's strtok, sprintf, index, etc except (hopefully) better. If we can come up with an easy way to say "make this string 8 chars wide, center the characters in the string and uppercase the first letter" in a very concise way, then print formatting is easy. Kind of like reducing printf to {return puts(sprintf(ARGS))}; Now if it's not very abbreviated, it's going to suck. printf was very brief. C++ wasn't as brief, but with the exception that some modifiers were persistent and some were one shot, it conveyed the idea fairly elegantly. It needs some work though in terms of ease of use. I've found the single argument, overloaded write/writeln scheme to be a bit clumsy. With good string formating, the printing may be less of an issue sincec we can build the string and print it as a single argument, but I still believe there will be needless memory/cpu overhead with such a scheme. If you do a lot of i/o, you'll run the cpu ragged collecting the temporaries when you're not in i/o wait.
 Another thought.  Instead of only making this work for char[], you could make
it work for all array types.  Earlier, in a
 discussion about casts, somebody talked about actually passing a type (not a
value) as a parameter into a cast function:
 
 int i = cast(int,foo);
I think that got shot down because the type could not be determined until the semantic pass.
 We could do a similar thing with the . operator on arrays.
 
 char[] str = "" . foo . bar . baz;
Dot won't work. It could be a member dereference, or it could be a concatenation. Actually, perl 6 is switching . to _ so that they switch -> to . like all other OO languages. That's academic though. We can find another operator.
 Is expanded to:
 
 char[] str = "" + format(char[],foo) + format(char[],bar) + format(char[],baz);
 
 I thought about using the cast syntax, but that becomes cloudy when the
argument you're trying to format is a pointer or another
 array.
Actually, format is looking a lot like a synonym for cast. How would you use the scheme to justify, set field width, or print a number in hex?
         I'm also having trouble being objective coming up with something looks
 like it fits D's ideology.  I'm real partial to C++'s i/o and in D
 that's right out.  I like the suggestion you had above for %magic on
 object types but I don't think it goes far enough.
I Walter doesn't mind the overloaded . operator idea, you could use << and >> instead: char[] str = "" . foo; becomes char[] str = "" + format(char[],foo); and str >> foo; becomes foo = extract(typeof(foo),str); Or something like that. Still no (good) solution for format specifiers.
It's not that I'm fixated on the >>/<< operators. It's that I am fixated on expressions as opposed to calls. I don't see a clean way to do that in a library w/o overloading operators or variable arguments. I also like the fact that user defined type can be printed like any first class Object. Treating Objects like first class types has been met with near hostility here. I think the might be a good hint that D i/o probably should try to blur that line no matter how much I may want to. Dan
Oct 19 2001
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 Russ Lewis wrote:

 The key with strong typing is that you know what you know and you don't have
to make wild guesses or assumptions.  When the %z (or
 whatever) magic is passed, if the typesafe printf sees that the matching
argument is anything but a pointer to an object, then it
 throws an exception.  If it is an object, then it calls toString with the
(possibly zero length) char[] array that is the %z format
 qualifier.  This call goes right to the overloaded version of this function in
the class (remember, the compiler is smart enough to
 make all functions virtual).  This class is then responsible for dealing with
the qualifier.

 If your class doesn't deal with qualifiers, then you throw the exception if it
is nonzero length.  If you do, then you throw an
 exception if you don't recognize the format.  Etc...

 Please clarify if I'm missing something, but this seems 100% typesafe to me -
provided that the underlying printf varargs
 architecture is also typesafe.
Well are typesafe in that it will always be a descendant of Object. But you can't be more specific. (I don't mean just any Fruit. I want an Apple!) You will always get the streamify routine for the Object you pass, but I guess I would like to say what type of Object I expect and get en error otherwise, like you would with builtin types. Not a show stopper by any means.
Remember that all member functions of classes are automatically virtual in D (when necessary). Thus, if you pass a Fruit* pointer (which is cast down to an Object*), then when you call toString(), you get Fruit::toString. You get complete control. And if the formatter arguments you pass with it don't make sense for Fruit::toString, then it throws an exception.
 Well, I guess I would rather that D would always print a char as a
 char/glyph, unless I cast it to something else (like an unsigned) in
 which case I would like to to print way ever I passed it.  I do agree
 that in some cases there is format information that is useful and not
 associated to the type (unless we attach a format property; probably
 overkill) like hex/oct/dec, left/right/center, and the like, but I don't
 think I like the use of the format character as an end run around a
 cast.  I C you didn't have a choice.  printf only saw ints.
Makes sense. We just have to implement the printf() routine...but I agree that this is a good default.
 We could go with the Perl syntax here (shudder).  Use the dot operator to
concatenate (formatted) strings.  Any time that the
 compiler sees something of the form:

 char[] . foo

 It converts it internally to

 char[] + format(foo)

 where format(...) is a series of library routines that convert various things
into char[].  The format(object) just calls
 toString() to do it.  But now we have the problem of how to do format

Actually, this isn't how perl does formatting. Your choices in perl are (s)printf and format lines like:
I meant it not as a formatting routine, but as an easy concatenation routine. If you used char[] . foo , it would call format to convert foo to a char[], then concatenate the arrays. If you wanted explicit formating strings, you would call char[] . format(foo, options)
 Ofc, this all requires that Walter implement some special case code for
char[]...but one of the stated objectives of D was
 "Languages should be good at handling strings."
Oh, for string handling. I was thinking of something comparable to C's strtok, sprintf, index, etc except (hopefully) better. If we can come up with an easy way to say "make this string 8 chars wide, center the characters in the string and uppercase the first letter" in a very concise way, then print formatting is easy.
Unfortunately, we can't really describe it easily even in English...not terribly likely we're going to be able to better in a programming language without creating some weird new language (like printf did). :(
 Kind of like reducing printf to {return puts(sprintf(ARGS))};
BTW, as you imply here, I very much think that sprintf should NOT take the buffer as an argument, but instead should return the string it creates. Either that or require arrays (no pointers). I don't want to mess with buffer overflows anymore. Or with having to calculate ahead of time how much buffer space I'll need.
 Is expanded to:

 char[] str = "" + format(char[],foo) + format(char[],bar) + format(char[],baz);

 I thought about using the cast syntax, but that becomes cloudy when the
argument you're trying to format is a pointer or another
 array.
Actually, format is looking a lot like a synonym for cast. How would you use the scheme to justify, set field width, or print a number in hex?
By adding an extra argument that allows the user to pass format specifiers. The problem with unifying format and cast is: int i = ...; char *ptr = format(char*,i); char[] str = format(char[],i); The first should cast the value i into a ptr whose address is i. The latter should create a char[] which contains a string which is the decimal representation of i. We need to keep these totally distinct. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 19 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
         Well, at the very least, I'd like to have a toStream method
that can
 default to toString.  The human readable format is not always the
 best/most complete way to store to a file.
I hope the hell somebody goes through D and standardizes capitalization before it goes public, or better yet get rid of case sensitivity. That was one of the few things I really liked about Pascal and that I now miss.
 I'm not sure really.  I guess I was thinking:

 form F = <manipulator(s)>;        // custom
 stdio.print(v1, hex(v2), F(v3));  // default form, hex form, my custom
 form

 But think makes it difficult or applets nasty looking to apply multiple
 format manipulator to a single variable in the print statement.

 stdio.print(hex(width(3, right(v1))));  // lisp anyone

 I don't think I would want to have to declare a format type for every
 custom manipulation.  It would give us one of the worst drawbacks of
 COBOL's and perl's record based output without the benefits.

 I'm also having trouble being objective coming up with something looks
 like it fits D's ideology.  I'm real partial to C++'s i/o and in D
 that's right out.  I like the suggestion you had above for %magic on
 object types but I don't think it goes far enough.
 Would help to work out a formalized set of string ops to build upon?
 Most I/O will either be binary records or text strings I would assume.
 The binary output wouldn't need much in the way of formatting.  fixed
 record sizes, byte and bit ordering and that sort of thing should do.
 It's with the human readable that we worry and justification,
 representation, padding, text formation of complex types.  Maybe we
 would have a better feel for what feels right for printing strings in D
 once we get a feel for what it would be like to just muck around with
 strings in D.
Binary files could be built on strings (since strings don't rely on that nasty trailing NULL character to determine their length, as in C/C++). Strings in general should be easy to manipulate. There's only a few things you'd want to do to any string: field width, justification, alignment, and padding (all can be implemented once as a function and will otherwise work for all types) and for any printable item to change type of conversion applied (These can be simple function calls probably with overloading, such as hex(int) and hex(char). I don't know about anyone else here, but I'm all for building my strings in memory and then dumping them off in a block to the file or stdout or whatever at reasonable intervals (such as each line). Especially if you can somehow cause the string to reserve enough memory to hold the biggest batch before you start. The big advantage is you should be able to use these methods on all your strings, not just strings going to stdout/cout/whatever. That one separation enables every kind of stream to have formatting, without having any stream have to implement or even wrap printf. All you'd need is an equivalent to puts(). Sean
Nov 04 2001
parent a <a b.c> writes:
"Sean L. Palmer" wrote:
 
         Well, at the very least, I'd like to have a toStream method
that can
 default to toString.  The human readable format is not always the
 best/most complete way to store to a file.
I hope the hell somebody goes through D and standardizes capitalization before it goes public, or better yet get rid of case sensitivity. That was one of the few things I really liked about Pascal and that I now miss.
 I'm not sure really.  I guess I was thinking:

 form F = <manipulator(s)>;        // custom
 stdio.print(v1, hex(v2), F(v3));  // default form, hex form, my custom
 form

 But think makes it difficult or applets nasty looking to apply multiple
 format manipulator to a single variable in the print statement.

 stdio.print(hex(width(3, right(v1))));  // lisp anyone

 I don't think I would want to have to declare a format type for every
 custom manipulation.  It would give us one of the worst drawbacks of
 COBOL's and perl's record based output without the benefits.

 I'm also having trouble being objective coming up with something looks
 like it fits D's ideology.  I'm real partial to C++'s i/o and in D
 that's right out.  I like the suggestion you had above for %magic on
 object types but I don't think it goes far enough.
 Would help to work out a formalized set of string ops to build upon?
 Most I/O will either be binary records or text strings I would assume.
 The binary output wouldn't need much in the way of formatting.  fixed
 record sizes, byte and bit ordering and that sort of thing should do.
 It's with the human readable that we worry and justification,
 representation, padding, text formation of complex types.  Maybe we
 would have a better feel for what feels right for printing strings in D
 once we get a feel for what it would be like to just muck around with
 strings in D.
Binary files could be built on strings (since strings don't rely on that nasty trailing NULL character to determine their length, as in C/C++).
The only problem is you have to make sure no character conversion silliness is going on. You also have to know what sizeof(char). You have to know that some deranged systems aren't converting '\n' into two characters. It's better to have a way to tell the code not to do any of that.
 Strings in general should be easy to manipulate.  There's only a few things
 you'd want to do to any string: field width, justification, alignment, and
 padding (all can be implemented once as a function and will otherwise work
 for all types) and for any printable item to change type of conversion
 applied (These can be simple function calls probably with overloading, such
 as hex(int) and hex(char).
This could be ok if the syntax doesn't get so complex as to take a way from what you are trying to format. printf fans/fiends often like how you get a feel for how the output will like by looking at the format string. I am also a little concerned about how to do alignment though before you send to the stream since that usual depends on stream state.
 I don't know about anyone else here, but I'm all for building my strings in
 memory and then dumping them off in a block to the file or stdout or
 whatever at reasonable intervals (such as each line).  Especially if you can
 somehow cause the string to reserve enough memory to hold the biggest batch
 before you start.  The big advantage is you should be able to use these
 methods on all your strings, not just strings going to stdout/cout/whatever.
 That one separation enables every kind of stream to have formatting, without
 having any stream have to implement or even wrap printf.  All you'd need is
 an equivalent to puts().
I've mentioned this before myself. The problem is that you have to know ahead of time what the largest batch is going to be. Some (many, I) would consider that an unnecessary pain. It also adds yet another buffer layer between the print and the final destination. This uses more clock cycles to build a temp string and it and a requires more memory to store our extra buffer. It will also require the the user the maintain the string to make sure it gets reused, or you have to count on GC to not go nuts when the program does a lot of io. printf didn't need to build such a buffer. It analyzed the arguments to be printed one at a time. What ever D has will be compared to printf, unless it is printf. If we replace printf, it better be good so we don't get as many people complaining about it as C++ iostreams has. Dan
Nov 04 2001
prev sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Are you implying that .toString() is a "magic method" that will 
be invoked automatically by printf?

That's a plausible approach.  It makes it more important that it 
be easy to concatenate strings.  Do remember to allow for usages 
such as:
    printf (x.asHead(1, 10) + "  " + y.asHead(1, 13) + ...);
or some suitable substitute.
N.B.:  here I was assuming that the asHead methods took the 

self layout formatting.
E.g., if one wanted to produce:
    This is  And This is
        x         y
    =======  ===========
one could do:
printf (x.asHead(1, 10) + "  " + y.asHead(1, 13) + "\n");
printf (x.asHead(2, 10) + "  " + y.asHead(2, 13) + "\n");
printf (x.asHead(3, 10) + "  " + y.asHead(3, 13) + "\n");

persuming that the objects could determine their names.
Oct 17 2001
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Charles Hixson wrote:

 Are you implying that .toString() is a "magic method" that will
 be invoked automatically by printf?
We have been using the term "magic" to refer to the printf format specifiers. We were thinking that we could add a new specifier to format objects. The idea is that when printf sees that specifier, it thinks the argument is a pointer to an object, and so calls the toString() method on it. It won't work - not safely, anyhow - unless we can get some sort of typesafe printing routine, so we don't mistakenly think that an int is a pointer or vice-versa. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 17 2001
prev sibling parent "Reg Whitton" <reg.whitton ifonline.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3BC477F3.F4D0452C deming-os.org...

 printf("value of my variable: %.8x\n",
         foo->GetOrthogonalBaz()
                ->Barrify()
                ->GetNormal(1,2,3)
                ->MakeHorizontal(false)
                ->GetX());

 I would challenge anyone here to write the cout code that matches the
formatting capabilities of printf. I
 would also challenge anybody to write cout code that expresses, as clearly
as the printf format string, how
 the stuff will actually look on the screen.

 IMHO, printf shines where you need complex formatting and/or the arguments
are complex. You are right! As it is said in the FAQ printf is damned useful. It also allows you to reuse those format strings again and again without having to use common routines to build up the messages. In fact I have worked with code that goes further down the format string road than printf, and would be impossible to do with C++ iostreams or string concatenation. When you do internationalisation you often need to be able to re-order where the arguments appear within the format. In your code you may put: display(getMessage(201), colour, item); in you English message file you may put: 201="You have selected a |0 |1." but in other languages you might need to name the item before giving its colour, so the message file might appear: 201="You have selected a |1, |0." // sorry I'm uni-lingual. However, I understand the frustration at printf. I have seen well tested software crash in live situations because printf had the wrong number of arguments and the program has segmented. You might say it cannot be well tested if it crashes, but most of the messages in a C program are there to deal with errors that will probably never happen. It is very difficult and unproductive to exercise all the error handling code. So where the compiler can check the format string against the arguments, I say please let it (I believe gcc does this with the -Wformat option). Unfortunately taking the messages out of the code (as above) and putting them into files would stop any attempt by the compiler to check the format string against parameter types. Where the compiler cannot see the format string, then I afraid it is upto the programmer to eyeball his code, or write tools to do this checking. I have written my own tools that parse the C sources and associated format strings, and then creates copies of the source with the printf function name replaced with one that mangles in the expected parameter types, and adds a related prototype. Compiling these copies flushes out the type errors.
Oct 11 2001