[comp.lang.c] not really Re: Style guides and portability

scs@adam.mit.edu (Steve Summit) (01/15/91)

In a hastily-added postscript to article
<1991Jan13.182655.17672@athena.mit.edu>, trying
overzealously to prove a point, I wrote:
>P.S. The answer to "How do you print something declared with
>`int32 bigint;' ?" is that you have to abandon printf in favor of
>something you define, like "print32".

In article <10608@hydra.Helsinki.FI>, Lars Wirzenius asks:
>Is there any problem in using 
>	printf("%ld", (long) bigint)
>other than that it's clumsy?

None whatsoever (as long as bigint is known to map to a built-in,
integral type, which in the problem under discussion, it was).
This is a much better solution.  (It's not even all that clumsy.)

                                            Steve Summit
                                            scs@adam.mit.edu

dkeisen@Gang-of-Four.Stanford.EDU (Dave Eisen) (01/17/91)

In article <1991Jan15.054540.1466@athena.mit.edu> scs@adam.mit.edu writes:
>
>In article <10608@hydra.Helsinki.FI>, Lars Wirzenius asks:
>>Is there any problem in using 
>>	printf("%ld", (long) bigint)
>>other than that it's clumsy?
>
>None whatsoever (as long as bigint is known to map to a built-in,
>integral type, which in the problem under discussion, it was).
>This is a much better solution.  (It's not even all that clumsy.)


It sorta defeats the purpose, doesn't it? 

The point of using a type like int32 is to avoid hardcoding the 
current compiler's idea of what a long looks like into the code and
here you are using "lf" and "long" thousands of times all over your
code. If you wanted to go this route, maybe something like 

#define BIGINT_FORMAT  "ld"
#define BIGINT_TYPE     long

would be appropriate.


It doesn't seem worth it to me. I expect I'll regret that decision
someday.




--
Dave Eisen                   "I strongly suspect the "right man" could go a
1447 N. Shoreline Blvd.       *long* way toward solving your "problem".
Mountain View, CA 94043         --- Dan Mocsney, offering himself for the job
(415) 967-5644                dkeisen@$Gang-of-Four.Stanford.EDU

steve@taumet.com (Stephen Clamage) (01/18/91)

dkeisen@Gang-of-Four.Stanford.EDU (Dave Eisen) writes:

>>In article <10608@hydra.Helsinki.FI>, Lars Wirzenius asks:
>>>Is there any problem in using 
>>>	printf("%ld", (long) bigint)
>>>other than that it's clumsy?
>It sorta defeats the purpose, doesn't it? 
>The point of using a type like int32 is to avoid hardcoding the 
>current compiler's idea of what a long looks like into the code and
>here you are using "lf" and "long" thousands of times all over your
>code. If you wanted to go this route, maybe something like 
>#define BIGINT_FORMAT  "ld"
>#define BIGINT_TYPE     long
>would be appropriate.

The source of the problem is inherent in printf-like functions, where
the receiving function does not know the type of the parameter being
passed, and has to be told in an auxiliary parameter (the format string).
Whenever you have the same piece of information kept in two places you
have synchronization problems -- in this case the type of the parameter
being passed is known by the compiler at the calling location, and has
to be duplicated by the programmer independently.

Other languages have solved this problem in different ways -- none of
them by using a printf-like function.

In Pascal, I/O is built into the language, so you can say
	write('the value is ', x);
which works no matter what the numeric type of x.  This solution is
not available in C.

In Modula-2, there is a separate output function for each data type.
This solution can be used in C, and with prototyping the compiler
will convert the numeric argument to the right type, or warn you
about dangerous conversions:
	int i;
	long l;
	void printi(int);
	void printl(long);
	printl(i);	/* i will be automatically converted to a long */
	printi(l);	/* compiler should warn about truncating l */

In C++, you can take advantage of function overloading to declare
multiple versions of a function with different parameter types, and
the compiler will call the right one:
	int i;
	long l;
	void print(int);
	void print(long);
	print(i);	// print(int) is called
	print(l);	// print(long) is called
You can also use one of the standard stream I/O packages, in which the <<
operator is overloaded for output and can be chained:
	#include <stream.h>	// or <iostream.h>
	int i;
	long l;
	cout << "the value is " << i;	// cout is standard output
	cout << "the value is " << l;

The key to minimizing porting difficulties is to avoid the inherent
problem in printf by one of the above solutions.  I find them cleaner
than
	printf(BIGINT_FORMAT, (BIGINT_TYPE) bigint);
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

dkeisen@Gang-of-Four.Stanford.EDU (Dave Eisen) (01/18/91)

I said (regarding setting up a generic bigint type with all the 
stuff needed to avoid having casts or other stuff sprinkled
throughout the source code):

>
>It doesn't seem worth it to me. I expect I'll regret that decision
>someday.
>

I take that back. I don't think I'll regret it.

I suspect I'll be coding in C++ (where this sort of stuff is much 
more convenient) long before I'll be porting any of my code to 
a machine with a radically different architecture. 



--
Dave Eisen                   "I strongly suspect the "right man" could go a
1447 N. Shoreline Blvd.       *long* way toward solving your "problem".
Mountain View, CA 94043         --- Dan Mocsney, offering himself for the job
(415) 967-5644                dkeisen@Gang-of-Four.Stanford.EDU

torek@elf.ee.lbl.gov (Chris Torek) (02/14/91)

(I realize I am reopening an old discussion here, which is rather like
reopening old wounds, but . . . .)

Given: a type abstraction (e.g., `typedef xxx Bigint', where xxx is
either int or long depending on the machine), various people suggest
that one way of printing these is:

>>>>	printf("%ld", (long) bigint)

while others suggest:
>>#define BIGINT_FORMAT  "ld"

In article <554@taumet.com> steve@taumet.com (Stephen Clamage) writes:
>The source of the problem is inherent in printf-like functions, where
>the receiving function does not know the type of the parameter being
>passed, and has to be told in an auxiliary parameter (the format string).
>Whenever you have the same piece of information kept in two places you
>have synchronization problems -- in this case the type of the parameter
>being passed is known by the compiler at the calling location, and has
>to be duplicated by the programmer independently.

All of this is true.

>Other languages have solved this problem in different ways -- none of
>them by using a printf-like function.

... and all of them have their drawbacks as well.

>In Pascal, I/O is built into the language, so you can say
>	write('the value is ', x);
>which works no matter what the numeric type of x.  This solution is
>not available in C.

Unfortunately, since I/O is built into the language, you are stuck with
whatever the language designer thought of.  Pascal, for instance, lacks
the equivalent of sprintf.  FORTRAN does not allow leading-prefix insertion
for hexadecimal numbers a la %+#x.

(Of course, the same argument works for printf, e.g., `why is there no
base-2 format?', but it is generally easier to add new formats to printf
than it is to add new syntaxes.)

>In Modula-2, there is a separate output function for each data type.

This tends to be avoided in other languages (and even in some extended
implementations of Modula-2) because it becomes verbose.  Often these
output functions also lack format directives, so there is no way to say
`this goes in a ten-space-wide field and always gets a leading sign'.

>The key to minimizing porting difficulties is to avoid the inherent
>problem in printf by one of the above solutions.  I find them cleaner
>than
>	printf(BIGINT_FORMAT, (BIGINT_TYPE) bigint);

The nice thing about BIGINT_FORMAT (equivalently, "%ld" plus a cast to
long)  is that you can get those format directives in (this bigint gets
a sign, that one gets leading zeros).

No matter what method you choose, you will eventually run across something
you forgot, so the most important thing is to make sure you can extend it.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov