www.digitalmars.com         C & C++   DMDScript  

D - typesafe printf

reply Russ Lewis <russ deming-os.org> writes:
I was thinking about printf, and was looking for some way to implement
it in D that would be typesafe.  I've played around with suggesting that
we add a .typeid to variables, and putting typeid information into the
varargs[] array, but I think I stumbled on something better:

Expand variable-argument list functions into recursive calls.  Thus, the
compiler would expand

printf("a = %d, b = %d, ans = %s", a,b,ans);
    into
printf(printf(printf("a = %d, b = %d, ans = %s",a),b),ans);

The innermost printf would return the string ", b = %d, ans = %s", the
next would return ", ans = %s", and the last would return an empty
string.  Then you might implement printf this way:

char[] printf(char [],...)
out(result)
{
    assert(result == "");
};

char[] printf(char[], int) {....};
char[] printf(char[], float) {....};
char[] printf(char[], char[]) {....};
char[] printf(char[], double) {....};
char[] printf(char[], void*) {....};
char[] printf(char[], Object) {....};  // calls Object.toString()

Any time that you pass an invalid argument type to the printf statement,
it will throw an InvalidPrintfArgumentException or some such.

I think that eventually, with optimizing compilers, the compiler might
unwrap this entire mess into an inline expression of some sort.  It
would be cool to compile something, have the compiler do some analysis,
and have it give a compile error:

foo.d line 947: printf() will throw InvalidPrintfArgumentException() =
"%d print format specifier used with pointer argument"
Aug 24 2001
next sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Russ Lewis wrote:
 I was thinking about printf, and was looking for some way to implement
 it in D that would be typesafe.  I've played around with suggesting ...
 "%d print format specifier used with pointer argument"
 

class that wanted to could implement it as it choose. And the default implementation could just call toString. Then when printf interpreted the format string, it could call to toPrint method of the class. You could even have a default action of "append to the end of the composed string" to handle the case where not enough formats were provided.
Aug 24 2001
parent reply Russ Lewis <russ deming-os.org> writes:
Charles Hixson wrote:

 Russ Lewis wrote:
 I was thinking about printf, and was looking for some way to implement
 it in D that would be typesafe.  I've played around with suggesting ...
 "%d print format specifier used with pointer argument"

class that wanted to could implement it as it choose. And the default implementation could just call toString. Then when printf interpreted the format string, it could call to toPrint method of the class. You could even have a default action of "append to the end of the composed string" to handle the case where not enough formats were provided.

I'm sorry but I'm not sure how this solves the problem of making printf() typesafe. How does printf know the type of the argument so that it knows which toPrint() to call?
Aug 24 2001
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Russ Lewis wrote:
 Charles Hixson wrote:
 
 
Russ Lewis wrote:

I was thinking about printf, and was looking for some way to implement
it in D that would be typesafe.  I've played around with suggesting ...
"%d print format specifier used with pointer argument"

class that wanted to could implement it as it choose. And the default implementation could just call toString. Then when printf interpreted the format string, it could call to toPrint method of the class. You could even have a default action of "append to the end of the composed string" to handle the case where not enough formats were provided.

I'm sorry but I'm not sure how this solves the problem of making printf() typesafe. How does printf know the type of the argument so that it knows which toPrint() to call?

printf just calls the toPrint method of each class in turn. It's the job of the class to figure out how to print itself. So if you say: printf ("13%5% rummaging around %3.1% picnic tables", "bears", 23); this would be parsed into: puts("13" + "bears".toPrint("5") + " rummaging around " + 23.toPrint("3.1") + " picnic tables"); Which looks typesafe to me. Of course, Integer would need to decide what to do with a format item of "3.1", but that's Integer's problem.
Aug 27 2001
parent reply Russ Lewis <russ deming-os.org> writes:
Charles Hixson wrote:

 Russ Lewis wrote:
 Charles Hixson wrote:


Russ Lewis wrote:

I was thinking about printf, and was looking for some way to implement
it in D that would be typesafe.  I've played around with suggesting ...
"%d print format specifier used with pointer argument"

class that wanted to could implement it as it choose. And the default implementation could just call toString. Then when printf interpreted the format string, it could call to toPrint method of the class. You could even have a default action of "append to the end of the composed string" to handle the case where not enough formats were provided.

I'm sorry but I'm not sure how this solves the problem of making printf() typesafe. How does printf know the type of the argument so that it knows which toPrint() to call?

printf just calls the toPrint method of each class in turn. It's the job of the class to figure out how to print itself. So if you say: printf ("13%5% rummaging around %3.1% picnic tables", "bears", 23); this would be parsed into: puts("13" + "bears".toPrint("5") + " rummaging around " + 23.toPrint("3.1") + " picnic tables"); Which looks typesafe to me. Of course, Integer would need to decide what to do with a format item of "3.1", but that's Integer's problem.

Ok, I see. You're talking, like me, about compiler expansion of the printf into another expression that does NOT use variable numbers of arguments and is typesafe. Functionally, I think yours would work (you expand printf into the Java style of printing), but it requires (I would guess) a lot more smarts in the compiler. With a recursive call, (nearly) all of the smarts are in the library rather than the compiler.
Aug 27 2001
parent reply "John Carney" <john.carney pacific.net.au> writes:
"Russ Lewis" <russ deming-os.org> wrote in message
news:3B8A963E.52BCC7C2 deming-os.org...
 Charles Hixson wrote:

 Russ Lewis wrote:
 Charles Hixson wrote:


Russ Lewis wrote:

I was thinking about printf, and was looking for some way to





it in D that would be typesafe.  I've played around with suggesting





"%d print format specifier used with pointer argument"

class that wanted to could implement it as it choose. And the default implementation could just call toString. Then when printf interpreted the format string, it could call to toPrint method of the class. You could even have a default action of "append to the end of the composed string" to handle the case where not enough formats were





I'm sorry but I'm not sure how this solves the problem of making



 typesafe.  How does printf know the type of the argument so that it



 toPrint() to call?

printf just calls the toPrint method of each class in turn. It's the job of the class to figure out how to print itself. So if you say: printf ("13%5% rummaging around %3.1% picnic tables", "bears", 23); this would be parsed into: puts("13" + "bears".toPrint("5") + " rummaging around " + 23.toPrint("3.1") + " picnic tables"); Which looks typesafe to me. Of course, Integer would need to decide what to do with a format item of "3.1", but that's Integer's problem.

Ok, I see. You're talking, like me, about compiler expansion of the

 another expression that does NOT use variable numbers of arguments and is

 Functionally, I think yours would work (you expand printf into the Java

 printing), but it requires (I would guess) a lot more smarts in the

 recursive call, (nearly) all of the smarts are in the library rather than

 compiler.

Huh? I'd say just the opposite. It's the library that's picking apart the string, not the compiler. I must say I like the toPrint() solution much better because it doesn't involve any implicit recursion. Regards, John Carney.
Aug 28 2001
parent reply Russ Lewis <russ deming-os.org> writes:
John Carney wrote:

 Functionally, I think yours would work (you expand printf into the Java

 printing), but it requires (I would guess) a lot more smarts in the

 recursive call, (nearly) all of the smarts are in the library rather than

 compiler.

Huh? I'd say just the opposite. It's the library that's picking apart the string, not the compiler. I must say I like the toPrint() solution much better because it doesn't involve any implicit recursion.

What I was getting at was that with your solution (where the complier picks apart the string and recreates it) the compiler must know a lot about printf and its format specifiers. At least, it knows a lot about the % character. I'm thinking that it would be better to have a design where the compiler knew nothing about what the function was for or how it works. This allows the compiler to define a single syntax for variable-argument lists and have it work for any range of functions people might implement. This also simplifies compiler implementation by allowing it less knowledge of the library. A simple compiler is nearly always a more correct compiler.
Aug 28 2001
parent reply "John Carney" <john.carney pacific.net.au> writes:
"Russ Lewis" <russ deming-os.org> wrote in message
news:3B8BB246.735ECA86 deming-os.org...
 John Carney wrote:

 Huh? I'd say just the opposite. It's the library that's picking apart


 string, not the compiler. I must say I like the toPrint() solution much
 better because it doesn't involve any implicit recursion.

What I was getting at was that with your solution (where the complier

 apart the string and recreates it) the compiler must know a lot about

 its format specifiers.  At least, it knows a lot about the % character.

Bzzzt! Who said anything about the compiler picking apart the string? Look up there where it says "It's the library that's picking apart the string, not the compiler." Why does the compiler need to know anything about how printf works for the "toPrint()" solution to work? Besides, it's not *my* solution - though I do wish it was :) Mind you, I would prefer "format()" to "toPrint()".
 I'm thinking that it would be better to have a design where the compiler

 nothing about what the function was for or how it works.  This allows the
 compiler to define a single syntax for variable-argument lists and have it

 for any range of functions people might implement.

 This also simplifies compiler implementation by allowing it less knowledge

 the library.  A simple compiler is nearly always a more correct compiler.

printf ("And we're %d%% with you on that one\n", 10 * 10)
Aug 29 2001
parent reply Russ Lewis <russ deming-os.org> writes:
John Carney wrote:

 "Russ Lewis" <russ deming-os.org> wrote in message
 news:3B8BB246.735ECA86 deming-os.org...
 What I was getting at was that with your solution (where the complier

 apart the string and recreates it) the compiler must know a lot about

 its format specifiers.  At least, it knows a lot about the % character.

Bzzzt! Who said anything about the compiler picking apart the string? Look up there where it says "It's the library that's picking apart the string, not the compiler." Why does the compiler need to know anything about how printf works for the "toPrint()" solution to work?

I get the feeling that one of us is missing something. Hope it isn't me :) If you have the library pick apart the string, the it's doing it at runtime, right? That means that it will need type information at runtime. That's the problem with printf and the variable argument list it uses. If it sees a variable on the stack of size 4 and the data is 0x30310A00, it can't tell if is unsigned long l = 0x30310A00 char buf[] = "01\n" or Foo *myPtr = new Foo; It's just binary data. To do library parsing of the string, we have to include runtime type identification, which is UGLY. To really make it typesafe, you need to use the compiler to resolve a variable-argument call down to a series of known-argument calls with good typechecking. Then the library will know what you passed it, because the compiler calls the right function.
 printf ("And we're %d%% with you on that one\n", 10 * 10)

LOL!
Aug 29 2001
parent "John Carney" <john.carney pacific.net.au> writes:
"Russ Lewis" <russ deming-os.org> wrote in message
news:3B8D3EBD.F54188D2 deming-os.org...
 John Carney wrote:

 "Russ Lewis" <russ deming-os.org> wrote in message
 news:3B8BB246.735ECA86 deming-os.org...


<snip>
 To really make it typesafe, you need to use the compiler to resolve a
 variable-argument call down to a series of known-argument calls with good
 typechecking.  Then the library will know what you passed it, because the
 compiler calls the right function.

I see your point. I still don't like it.
 printf ("And we're %d%% with you on that one\n", 10 * 10)

LOL!

Aug 30 2001
prev sibling parent Chris Friesen <cfriesen nortelnetworks.com> writes:
Russ Lewis wrote:

 Expand variable-argument list functions into recursive calls.  Thus, the
 compiler would expand
 
 printf("a = %d, b = %d, ans = %s", a,b,ans);
     into
 printf(printf(printf("a = %d, b = %d, ans = %s",a),b),ans);
 
 The innermost printf would return the string ", b = %d, ans = %s", the
 next would return ", ans = %s", and the last would return an empty
 string.  Then you might implement printf this way:
 
 char[] printf(char [],...)
 out(result)
 {
     assert(result == "");
 };
 
 char[] printf(char[], int) {....};
 char[] printf(char[], float) {....};
 char[] printf(char[], char[]) {....};
 char[] printf(char[], double) {....};
 char[] printf(char[], void*) {....};
 char[] printf(char[], Object) {....};  // calls Object.toString()

This sounds like an interesting idea. I think it could work out quite nicely. -- Chris Friesen | MailStop: 043/33/F10 Nortel Networks | work: (613) 765-0557 3500 Carling Avenue | fax: (613) 765-2986 Nepean, ON K2H 8E9 Canada | email: cfriesen nortelnetworks.com
Aug 29 2001