Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.std.c > #595 > unrolled thread

PRI* considered harmful

Started by"Greg A. Woods" <woods@robohack.org>
First post2011-07-06 19:58 -0700
Last post2011-07-09 20:30 +0100
Articles 20 on this page of 66 — 14 participants

Back to article view | Back to comp.std.c


Contents

  PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-06 19:58 -0700
    Re: PRI* considered harmful William Ahern <william@wilbur.25thandClement.com> - 2011-07-06 20:38 -0700
      Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-06 22:26 -0700
        Re: PRI* considered harmful Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-07-07 08:52 +0200
          Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-07 14:11 -0700
            Re: PRI* considered harmful Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-07-08 11:58 +0200
          Re: PRI* considered harmful lawrence.jones@siemens.com - 2011-07-07 17:58 -0400
            Re: PRI* considered harmful Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-07-08 12:02 +0200
        Re: PRI* considered harmful lawrence.jones@siemens.com - 2011-07-07 17:57 -0400
          Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-07 15:42 -0700
            Re: PRI* considered harmful lawrence.jones@siemens.com - 2011-07-08 08:56 -0400
    Re: PRI* considered harmful James Kuyper <jameskuyper@verizon.net> - 2011-07-07 06:36 -0400
      Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-07 12:59 -0700
        Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-07 14:12 -0700
          Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-07 15:49 -0700
            Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-07 16:46 -0700
              Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-07 18:26 -0700
                Re: PRI* considered harmful James Kuyper <jameskuyper@verizon.net> - 2011-07-07 22:35 -0400
                  Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-07 20:58 -0700
                    Re: PRI* considered harmful Wojtek Lerch <wojtek_l@yahoo.ca> - 2011-07-08 00:28 -0400
                    Re: PRI* considered harmful James Kuyper <jameskuyper@verizon.net> - 2011-07-08 07:41 -0400
                      Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-08 23:05 -0700
                        Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 00:50 -0700
                        Re: [incorrectly implementd] PRI* considered harmful James Kuyper <jameskuyper@verizon.net> - 2011-07-09 06:38 -0400
                        Re: PRI* considered harmful Hans-Bernhard Bröker <HBBroeker@t-online.de> - 2011-07-09 21:07 +0200
                    Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-08 09:52 -0700
                Re: PRI* considered harmful Richard Kettlewell <rjk@greenend.org.uk> - 2011-07-08 11:45 +0100
                  Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-08 23:14 -0700
                    Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 00:47 -0700
                    Re: PRI* considered harmful Richard Kettlewell <rjk@greenend.org.uk> - 2011-07-09 10:51 +0100
                    Re: PRI* considered harmful James Kuyper <jameskuyper@verizon.net> - 2011-07-09 06:43 -0400
                Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-08 09:49 -0700
                  Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-08 23:24 -0700
                    Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 00:45 -0700
                      Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-09 16:18 -0700
                        Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 18:57 -0700
                          Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-13 09:37 -0700
                    Re: PRI* considered harmful James Kuyper <jameskuyper@verizon.net> - 2011-07-09 06:46 -0400
                    Re: PRI* considered harmful Harald van Dijk <truedfx@gmail.com> - 2011-07-09 08:09 -0700
                      Re: PRI* considered harmful Phil Carmody <thefatphil_demunged@yahoo.co.uk> - 2011-07-11 16:00 +0300
                    Re: PRI* considered harmful Hans-Bernhard Bröker <HBBroeker@t-online.de> - 2011-07-09 20:59 +0200
                      Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 12:22 -0700
                        Re: PRI* considered harmful Hans-Bernhard Bröker <HBBroeker@t-online.de> - 2011-07-09 21:32 +0200
                          Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 12:54 -0700
                            Re: PRI* considered harmful Marcin Grzegorczyk <mgrzegor@poczta.onet.pl> - 2011-07-09 23:39 +0200
                              Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-09 16:56 -0700
                            Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-09 16:52 -0700
                              Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 18:53 -0700
                            Re: PRI* considered harmful Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-07-10 14:12 +0200
                              Re: PRI* considered harmful Marcin Grzegorczyk <mgrzegor@poczta.onet.pl> - 2011-07-11 20:22 +0200
                          Re: PRI* considered harmful Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-07-10 13:57 +0200
                      Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-09 16:49 -0700
                    Re: PRI* considered harmful Jens Schweikhardt <usenet@schweikhardt.net> - 2011-07-19 19:28 +0000
        Re: PRI* considered harmful Tim Rentsch <txr@alumni.caltech.edu> - 2011-07-07 14:13 -0700
    Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 01:04 -0700
      Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-09 17:23 -0700
        Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-09 19:03 -0700
          Re: PRI* considered harmful Marcin Grzegorczyk <mgrzegor@poczta.onet.pl> - 2011-07-11 20:40 +0200
            Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-11 11:56 -0700
              Re: PRI* considered harmful Marcin Grzegorczyk <mgrzegor@poczta.onet.pl> - 2011-07-11 21:40 +0200
                Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-11 13:16 -0700
                  Re: PRI* considered harmful Marcin Grzegorczyk <mgrzegor@poczta.onet.pl> - 2011-07-11 23:29 +0200
                    Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-11 14:46 -0700
                Re: PRI* considered harmful "Greg A. Woods" <woods@robohack.org> - 2011-07-12 22:18 -0700
                  Re: PRI* considered harmful Keith Thompson <kst-u@mib.org> - 2011-07-13 09:00 -0700
    Re: PRI* considered harmful Richard Kettlewell <rjk@greenend.org.uk> - 2011-07-09 20:30 +0100

Page 1 of 4  [1] 2 3 4  Next page →


#595 — PRI* considered harmful

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-06 19:58 -0700
SubjectPRI* considered harmful
Message-ID<87mxgqhk3x.fsf@once.weird.com>
I've been scratching my head over this ever since I first tried to
actually use the PRI* macros in real-world code, and a recent discussion
about what to do w.r.t. some systems are increasing time_t to be 64 bits
caused me to go searching the Internets further, but to no avail.
Everyone is inventing their own hokey incompatible schemes and nobody is
really benefiting from any improvement in the standard.


	PRI* Considered Harmful:

In order to avoid warnings from some versions of GCC (and perhaps other
compilers as well -- I haven't had the recent pleasure) on some types of
systems you will still have to cast some variables declared using
standard typedef names, such as time_t, to the appropriate int*_t type,
even when using an appropriate sized PRI* macro with printf(), eg.g. as
in:

	typedef time_t long;

	time_t t = 0;

	printf("%" PRId32 "\n", (int32_t) t);

That's because often PRId32 is "d", but on the same systems time_t is
known deep down as a "long", not an "int", and GCC doesn't like the use
of a "long" with "%d" (nor should it).

On other systems PRId32 will be "ld" (sadly even on some 32-bit systems
with 32-bit int and long!), and if "int32_t" isn't also known as "long"
down under then you're still screwed.

So, this suggested work-around of casting with the matching-sized int*_t
will only work if the underlying definitions of int*_t match the way
PRId* are defined, and I'm not convinced this is true on all systems
today.

Obviously this suggestion is only half-baked since the whole idea of
typedefs such as time_t is to allow their underlying definition to
change without having to change code everywhere their offspring are
used.  Clearly with the above use of every related use of PRId32 and
int32_t would have to change when time_t becomes int64_t.

The next suggestion is inevitably to define a pair of macros:

	#if (!defined(HAVE_INTTYPES_H) &&				\
	     (defined(__STDC_VERSION__) &&				\
	      ((__STDC_VERSION__ - 0) >= 199901L) ||			\
	      defined(_ISOC99_SOURCE) ||				\
	      (defined(__NetBSD_Prereq__) && __NetBSD_Prereq__(1,6,0)) ||\
	      (defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 106000000))))	/* etc. */
	# define HAVE_INTTYPES_H	1
	#endif

	#ifdef HAVE_INTTYPES_H
	# include <inttypes.h>
	#endif
	#include <time.h>

	/* assume time_t is always signed */
	#ifndef PRIdTIME
	# if SIZEOF_TIME_T == SIZEOF_INT32_T
	#  define INT_TIME_T	int32_t
	#  define PRIdTIME	PRId32
	# elif SIZEOF_TIME_T == SIZEOF_INT64_T
	#  define INT_TIME_T	int64_t
	#  define PRIdTIME	PRId64
	# endif
	#endif

And of course have some configure script that's run before the build
or other template files supply the definitions for the SIZEOF_* macros.

Now we can do:

	printf("%" PRIdTIME_T "\n", (INT_TIME_T) t);

Unfortunately this leaves approximately everyone re-inventing this wheel
every time, even for the standard typedef names!


	A Teeny Improvement:

I've also seen it suggested that _every_ non-base-integer type _always_
be cast to [u]intmax_t and printed using PRI[ud]MAX:

	printf("%" PRIuMAX, (uintmax_t) uival);
	printf("%" PRIdMAX, (intmax_t) t); /* for signed values */

Now we're safe for every possible underlying type of any typedef name,
home baked, or standardised.

However if you're always going to do that, then it's far simpler to
avoid the possibly confusing and unnecessarily verbose (and potentially
incorrectly defined) use of PRI*MAX and just use "%jd" or "%ju" as
necessary.  The only advantage of PRI*MAX is that it'll make your code
more easily ported to non-conforming environments (i.e. those without
"%jd" et al, and even if PRI*MAX are defined wrong you can still fix it
with #undef).

FYI:  The theory here is that the potentially additional heavy lifting
by printf() is trivial compared to the programmer overhead of trying to
find and/or define, and/or use, the right PRI* macros.


	A Better Improvement(?):

Too bad printf(3) et al don't have format flag akin to '*' which allows
you to pass the sizeof() and the signedness of an integer type to it so
that you don't have to hard-code the integer size in the format
string....

	int128_t foo = -1;
	uint64_t ubar = 1;

	#define issigned(v)	(((typeof(v)) -1 < 0) ? true : false)
	#define INTPARAM(v)	(int) issigned(v), (int) sizeof(v), v

	printf("%Si %Si\n", INTPARAM(foo), INTPARAM(ubar));

(I chose "%i" to suggest somewhat that it's different from "%d" or "%u",
i.e. in that it takes the signedness from the 'S' flag (since I don't
see %i used very often in the code I read -- it's definitely the new kid
on the block, and perhaps it can grow up more with a new meaning), but
of course it need not be -- "%d" and "%u" can also take the sign flag
using the 'S' flag, but that may confuse the novices more often than not.)

(also, issigned should become a new keyword, as should typeof from GCC,
though with the former, the latter isn't needed here :-))


Does anyone else think this would be worthwhile?  Better ideas anyone?

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [next] | [standalone]


#596

FromWilliam Ahern <william@wilbur.25thandClement.com>
Date2011-07-06 20:38 -0700
Message-ID<pamfe8-4ig.ln1@wilbur.25thandClement.com>
In reply to#595
Greg A. Woods <woods@robohack.org> wrote:
> I've been scratching my head over this ever since I first tried to
> actually use the PRI* macros in real-world code, and a recent discussion
> about what to do w.r.t. some systems are increasing time_t to be 64 bits
> caused me to go searching the Internets further, but to no avail.
> Everyone is inventing their own hokey incompatible schemes and nobody is
> really benefiting from any improvement in the standard.
<snip>
> I've also seen it suggested that _every_ non-base-integer type _always_
> be cast to [u]intmax_t and printed using PRI[ud]MAX:

>         printf("%" PRIuMAX, (uintmax_t) uival);
>         printf("%" PRIdMAX, (intmax_t) t); /* for signed values */

> Now we're safe for every possible underlying type of any typedef name,
> home baked, or standardised.

time_t could be also be double, not just an integer. This is why difftime()
exists. Note that it returns a double. PRI-type macros can't really solve
the problem because of the different format specifications between double
and integer types.

The proper answer is strftime().

The real problem is converting a time_t to struct tm because of the use of
globals in gmtime() and localtime(). As far as I can tell this problem isn't
directly addressed yet in the latest C1x draft I have locally,
notwithstanding the Annex K extension library. Visual Studio implements
parts of Annex K, including gmtime_s() and localtime_s(). POSIX defines
gmtime_r() and localtime_r(), from which the Annex K routines are presumably
drawn.

[toc] | [prev] | [next] | [standalone]


#598

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-06 22:26 -0700
Message-ID<87box6hdaj.fsf@once.weird.com>
In reply to#596
William Ahern <william@wilbur.25thandClement.com> writes:
> 
> Greg A. Woods <woods@robohack.org> wrote:
> > I've also seen it suggested that _every_ non-base-integer type _always_
> > be cast to [u]intmax_t and printed using PRI[ud]MAX:
> > 
> >         printf("%" PRIuMAX, (uintmax_t) uival);
> >         printf("%" PRIdMAX, (intmax_t) t); /* for signed values */
> > 
> > Now we're safe for every possible underlying type of any typedef name,
> > home baked, or standardised.
>
> time_t could be also be double, not just an integer. This is why difftime()
> exists. Note that it returns a double. PRI-type macros can't really solve
> the problem because of the different format specifications between double
> and integer types.

I personally think it would be insane for any host environment to try to
make time_t a non-integer type, but yes, that's another aspect of PRI*
considered harmful :-)

> The proper answer is strftime().

Nope -- time_t is just symptomatic of the problem -- there are other
typedef names in the standard (wchar_t for example), and indeed dozens
of non-standard similar names in any real-world implementation.

Besides, strftime() (at least in C99) cannot so far as I know print the
canonical numerical value of a time_t!

Do you propose a strf*() for every one?  Especially when for most of
them the numerical value is the only way they are printed?

I think you may have missed my point entirely!

I want to get rid of (deprecate) PRI* entirely, thus my topic heading.

The _main_ argument about the insanity of printf(), and all the
confusion it has caused generations of C programmers, has always been
about getting the right format characters to match the sign and the size
of integer parameters.  Everything else about printf() is really quite
trivial in comparison and can be taught quite easily.  It's easy to tell
a student that a string is not an integer, etc., but you can see
confusion about sign and size of integer types vs. printf() everywhere,
all of the time.

This (i.e. really all of stdarg) is the one place where the true
low-level nature of C still shines through with unbridled enthusiasm.
This isn't necessarily a bad thing, but we have never really truly done
anything useful to help at least make the worst parts, for the worst
standard functions, any better.  My proposal doesn't mess with the
language per se -- I just add a facility to printf() (and I suppose
scanf()) which would allow the programmer to trivially ensure the format
strings always match the magnitude and sign of the values no matter what
some silly maintainer might do to the typedefs (within reason --
i.e. within keeping to integer types).

Indeed if it were not for the legacy I would require that all parameters
for all format characters for all numerical types always be accompanied
by a sign flag and an indication of how many "units" should be extracted
or pushed from/to the parameter list by the likes of printf() and scanf().
If you want to go one further then include the "storage format" in with
the sign flag too (int, float, or pointer).

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [prev] | [next] | [standalone]


#599

FromJens Gustedt <Jens.Gustedt@loria.fr>
Date2011-07-07 08:52 +0200
Message-ID<4E155790.7020101@loria.fr>
In reply to#598
Am 07.07.2011 07:26, schrieb Greg A. Woods:
> This (i.e. really all of stdarg) is the one place where the true
> low-level nature of C still shines through with unbridled enthusiasm.
> This isn't necessarily a bad thing, but we have never really truly done
> anything useful to help at least make the worst parts, for the worst
> standard functions, any better.

But this is exactly where the problem is, not printf per se, but
stdarg.

The promotion rules are simply archaic and of no good, nowadays. All
the problem would go away if all integer type parameters would be
promoted to [u]intmax_t, all data pointer types to void* and all
floating types to long double.

I don't think that this has anything to do with "low-level" nature of
C, but with a wrong design choice that we carry around as a legacy.

Not that I am against it, but what you are proposing is just a cure
for a symptom, one of many symptoms for that particular legacy.

[toc] | [prev] | [next] | [standalone]


#619

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-07 14:11 -0700
Message-ID<87vcvdg5j7.fsf@once.weird.com>
In reply to#599
Jens Gustedt <Jens.Gustedt@loria.fr> writes:
> 
> Am 07.07.2011 07:26, schrieb Greg A. Woods:
>> This (i.e. really all of stdarg) is the one place where the true
>> low-level nature of C still shines through with unbridled enthusiasm.
>> This isn't necessarily a bad thing, but we have never really truly done
>> anything useful to help at least make the worst parts, for the worst
>> standard functions, any better.
>
> But this is exactly where the problem is, not printf per se, but
> stdarg.

The underlying problem, yes, but not the _real_ problem.  The real
problem is the one we see in questions from students and practitioners
every day, and that is with the confusion over integer size and sign
vs. printf() and scanf() format specifiers.


> The promotion rules are simply archaic and of no good, nowadays.

No, that's not right _at_ _all_.

> All
> the problem would go away if all integer type parameters would be
> promoted to [u]intmax_t, all data pointer types to void* and all
> floating types to long double.

So, you want new, different, promotion rules, not to get rid of the
promotion rules.  Sorry for my language, but that would be stupid and
would break WAY too much existing code, and it would raise ire from
everyone seriously concerned with performance.  We have already had more
than enough stupidity in more modern C standards which has caused well
enough breakage already in existing code.

There's nothing really wrong with the current promotion rules for
function parameters, though ideally they should be a little more
compatible with the original language, not a slight incompatible
variation and invention.  They're still confusing of course, but that's
the way they are.


> I don't think that this has anything to do with "low-level" nature of
> C, but with a wrong design choice that we carry around as a legacy.

By "low-level" I mean by the way C does not pass type information to the
caller (esp. for variable numbers of parameters) -- i.e. parameters are
stuffed on the stack or in registers by the caller and it's up to the
receiver to know what to expect.  This is dealt with in printf() and
scanf(), as well as in every other similar real-world situation like
this:  first let me tell you how I'm going to tell you what I'm going to
tell you.

Unfortunately though printf() doesn't do that very well sometimes, at
least not for human programmers, and even the compiler can't help other
than with non-standard non-required warnings in the case of integer
types that purposefully hide their width and sign from the programmer.

My proposal is simply to add further hints about the parameters which
follow.  I.e. to extend what is already done, and to do so in a reliable
and human-friendly way.

If you want to print(scan) a number, but you don't know what the
underlying type is, then you just use "%Si" and INTPARAM(ival) and you
will explicitly tell printf()/scanf() what type to expect.

Folks who worry about the cost of pushing three ints for every int don't
have to use the 'S' flag.  I don't want to take away anything -- just
add something new and very simple in an upwardly compatible way.


> Not that I am against it, but what you are proposing is just a cure
> for a symptom, one of many symptoms for that particular legacy.

I'm just trying to suggest a possible fix for what I've seen over the
past 20 years as a very common problem, as opposed to trying to change
any very important aspect of the fundamental language like you've
suggested doing.

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [prev] | [next] | [standalone]


#647

FromJens Gustedt <Jens.Gustedt@loria.fr>
Date2011-07-08 11:58 +0200
Message-ID<4E16D4CA.5090606@loria.fr>
In reply to#619
Am 07.07.2011 23:11, schrieb Greg A. Woods:
> So, you want new, different, promotion rules, not to get rid of the
> promotion rules.  Sorry for my language,

Not that I care much, but you could have formulated differently. The
discussion would benefit from a somewhat less aggressive tone.

> but that would be stupid and
> would break WAY too much existing code, and it would raise ire from
> everyone seriously concerned with performance.  We have already had more
> than enough stupidity in more modern C standards which has caused well
> enough breakage already in existing code.

Just to get that right, I didn't say that we should change the
promotions rules to what I described. I am very well aware of the
difficulties that this would introduce. I just wanted to point out
where the conceptual difficulty is.

If you like it or not, the argument promotion rules for functions
without prototypes are a mis-design. The only point where they
nowadays still are relevant are functions with variable argument
lists. And changing these argument promotion rules would only be
practically relevant for this class of functions, unfortunately a
bunch of standard functions among them.

For other functions, with a fixed number of arguments, nowadays
anybody serious should use prototypes so default argument promotion
doesn't apply.

Jens

[toc] | [prev] | [next] | [standalone]


#626

Fromlawrence.jones@siemens.com
Date2011-07-07 17:58 -0400
Message-ID<tomhe8-2co.ln1@jones.homeip.net>
In reply to#599
Jens Gustedt <Jens.Gustedt@loria.fr> wrote:
> 
> The promotion rules are simply archaic and of no good, nowadays. All
> the problem would go away if all integer type parameters would be
> promoted to [u]intmax_t, all data pointer types to void* and all
> floating types to long double.

That would work great as long as you don't care about upward
compatibility or efficiency.
-- 
Larry Jones

My life needs a rewind/erase button. -- Calvin

[toc] | [prev] | [next] | [standalone]


#646

FromJens Gustedt <Jens.Gustedt@loria.fr>
Date2011-07-08 12:02 +0200
Message-ID<4E16D5CF.2060208@loria.fr>
In reply to#626
Am 07.07.2011 23:58, schrieb lawrence.jones@siemens.com:
> Jens Gustedt <Jens.Gustedt@loria.fr> wrote:
>>
>> The promotion rules are simply archaic and of no good, nowadays. All
>> the problem would go away if all integer type parameters would be
>> promoted to [u]intmax_t, all data pointer types to void* and all
>> floating types to long double.
> 
> That would work great as long as you don't care about upward
> compatibility or efficiency.

(please see also my answer to Greg)

Compatibility I buy.

I was talking of argument promotion rules. Here, efficiency would only
be practically impacted for functions with variable argument
lists. All other functions are supposed to have prototypes,
nowadays. So this is a very limited field, not as dramatic as your
comment makes it look.

Jens

[toc] | [prev] | [next] | [standalone]


#625

Fromlawrence.jones@siemens.com
Date2011-07-07 17:57 -0400
Message-ID<lmmhe8-2co.ln1@jones.homeip.net>
In reply to#598
Greg A. Woods <woods@robohack.org> wrote:
> 
> I personally think it would be insane for any host environment to try to
> make time_t a non-integer type

I know of at least one implementation that used double, although it only
stored integer values in it (double had more precision than long).
-- 
Larry Jones

I always send Grandma a thank-you note right away.  ...Ever since she
sent me that empty box with the sarcastic note saying she was just
checking to see if the Postal Service was still working. -- Calvin

[toc] | [prev] | [next] | [standalone]


#628

FromKeith Thompson <kst-u@mib.org>
Date2011-07-07 15:42 -0700
Message-ID<lnliw9zp9c.fsf@nuthaus.mib.org>
In reply to#625
lawrence.jones@siemens.com writes:
> Greg A. Woods <woods@robohack.org> wrote:
>> I personally think it would be insane for any host environment to try to
>> make time_t a non-integer type
>
> I know of at least one implementation that used double, although it only
> stored integer values in it (double had more precision than long).

The standard says that both time_t and clock_t are "arithmetic
types".  In principle, they could be complex (or imaginary).
I suggest that it would be worth tweaking the wording to exclude
that possibility.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#655

Fromlawrence.jones@siemens.com
Date2011-07-08 08:56 -0400
Message-ID<pcbje8-fsp.ln1@jones.homeip.net>
In reply to#628
Keith Thompson <kst-u@mib.org> wrote:
> 
> The standard says that both time_t and clock_t are "arithmetic
> types".  In principle, they could be complex (or imaginary).
> I suggest that it would be worth tweaking the wording to exclude
> that possibility.

That's already been done: N1570 describes them as real types.
-- 
Larry Jones

Nobody knows how to pamper like a Mom. -- Calvin

[toc] | [prev] | [next] | [standalone]


#601

FromJames Kuyper <jameskuyper@verizon.net>
Date2011-07-07 06:36 -0400
Message-ID<iv427t$ed2$1@dont-email.me>
In reply to#595
On 07/06/2011 10:58 PM, Greg A. Woods wrote:
...
> In order to avoid warnings from some versions of GCC (and perhaps other
> compilers as well -- I haven't had the recent pleasure) on some types of
> systems you will still have to cast some variables declared using
> standard typedef names, such as time_t, to the appropriate int*_t type,
> even when using an appropriate sized PRI* macro with printf(), eg.g. as
> in:
> 
> 	typedef time_t long;

I assume that the above line, and only the above line, is supposed to be
from inside <time.h>? If so, you should have said so; if not, the above
line is a very bad idea.

> 	time_t t = 0;
> 
> 	printf("%" PRId32 "\n", (int32_t) t);
> 
> That's because often PRId32 is "d", but on the same systems time_t is
> known deep down as a "long", not an "int", and GCC doesn't like the use
> of a "long" with "%d" (nor should it).

The type of time_t should not matter, since you've casted the value of
't' to int32_t. You should only run into the kind of problem you're
referring to if int32_t is long, in which case PRId32 should be 'ld',
not 'd'. If it isn't, that's a non-conforming implementation of
<stdint.h>, not a defect in the concept of the PRI* macros.
The PRI* macros are clumsy and ugly, but the problem you describe isn't
one of their faults.

> On other systems PRId32 will be "ld" (sadly even on some 32-bit systems
> with 32-bit int and long!), and if "int32_t" isn't also known as "long"
> down under then you're still screwed.

Again, this implies that PRId32 is set incorrectly by those implementations.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#616

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-07 12:59 -0700
Message-ID<8762ndhnfs.fsf@once.weird.com>
In reply to#601
James Kuyper <jameskuyper@verizon.net> writes:

> On 07/06/2011 10:58 PM, Greg A. Woods wrote:
> ...
> The type of time_t should not matter, since you've casted the value of
> 't' to int32_t. You should only run into the kind of problem you're
> referring to if int32_t is long, in which case PRId32 should be 'ld',
> not 'd'. If it isn't, that's a non-conforming implementation of
> <stdint.h>, not a defect in the concept of the PRI* macros.

Indeed, that is what one would hope, but in the draft copy of the C99
standard which I have I cannot find anything which actually says that.

> The PRI* macros are clumsy and ugly, but the problem you describe isn't
> one of their faults.

Well I think their biggest fault is that they still require casts in all
places where typedef names are used, i.e. they do not cover all standard
typedef names for typedefs with base integer types.

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [prev] | [next] | [standalone]


#620

FromKeith Thompson <kst-u@mib.org>
Date2011-07-07 14:12 -0700
Message-ID<lntyaxztes.fsf@nuthaus.mib.org>
In reply to#616
"Greg A. Woods" <woods@robohack.org> writes:
> James Kuyper <jameskuyper@verizon.net> writes:
>> On 07/06/2011 10:58 PM, Greg A. Woods wrote:
>> ...
>> The type of time_t should not matter, since you've casted the value of
>> 't' to int32_t. You should only run into the kind of problem you're
>> referring to if int32_t is long, in which case PRId32 should be 'ld',
>> not 'd'. If it isn't, that's a non-conforming implementation of
>> <stdint.h>, not a defect in the concept of the PRI* macros.
>
> Indeed, that is what one would hope, but in the draft copy of the C99
> standard which I have I cannot find anything which actually says that.

7.8.1p1:

    Each of the following object-like macros expands to a character
    string literal containing a conversion specifier, possibly
    modified by a length modifier, suitable for use within the format
    argument of a formatted input/output function when converting
    the corresponding integer type.

PRI32d *must* be "suitable for use" with an int32_t argument.

(I suppose PRI32d could be defined as "ld" even if int32_t is a typedef
for int, but only if "%ld" actually works with an int32_t or int
argument.)

>> The PRI* macros are clumsy and ugly, but the problem you describe isn't
>> one of their faults.
>
> Well I think their biggest fault is that they still require casts in all
> places where typedef names are used, i.e. they do not cover all standard
> typedef names for typedefs with base integer types.

time_t wasn't a good example for your point, since it's only required to
be an arithmetic type.  The same applies to clock_t.  The problem in
your original code was converting a time_t to int32_t in the first
place.

You can print a time_t value, but it requires several tests to determine
what kind of type it is.  For example:

#include <stdio.h>
#include <stdint.h>
#include <time.h>
int main(void)
{
    time_t now = time(NULL);

    if ((time_t)1 / 2 == 0) {
        if ((time_t)-1 < (time_t)0) {
            printf("signed: %jd\n", (intmax_t)now);
        }
        else {
            printf("unsigned: %ju\n", (intmax_t)now);
        }
    }
    else {
        printf("floating-point: %Lg\n", (long double)now);
    }
    return 0;
}

The language and library don't make it *easy* to print raw time_t
values because there's rarely a good reason to do so.  That's what
strftime() is for.

There are modifiers for all the predefined types and for signed
and unsigned versions of intmax_t, size_t, and ptrdiff_t, and
there are PTR* macros for all the types defined in <stdint.h>.
The only library-defined *integer* type that isn't covered, as far
as I can tell, is wchar_t.  You wouldn't normally want to print that
as integer value anyway.  (And if you do, you can always convert
it to intmax_t or uintmax_t.)

Or you can write non-portable code that assumes time_t is an integer
type; sometimes that's good enough.

(Actually, my program here assumes that time_t isn't a complex type.
I think that's a reasonably safe assumption.)

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#629

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-07 15:49 -0700
Message-ID<87oc15g10i.fsf@once.weird.com>
In reply to#620
Keith Thompson <kst-u@mib.org> writes:
> 
> (I suppose PRI32d could be defined as "ld" even if int32_t is a typedef
> for int, but only if "%ld" actually works with an int32_t or int
> argument.)

Exactly, and thus a compiler that cares about portability must then
complain that format of "%ld" does not match type of "int", and
rightfully so.

You can't escape the casts with the PRI* macros even if you're just
trying to play with ints and longs.

(Maybe the standard should be fixed in this respect, but it wouldn't
really solve enough of the problems with the PRI* debacle.)


> The language and library don't make it *easy* to print raw time_t
> values because there's rarely a good reason to do so.  That's what
> strftime() is for.

It might be rare in your world, but it's far from rare in my world.

Also, I repeat, C99 strftime() is unable to print the value of a time_t
as an integer representation of seconds since the epoch of time_t.  (C99
does not have the '%s' specifier that many implementations do have.)


> The only library-defined *integer* type that isn't covered, as far
> as I can tell, is wchar_t.  You wouldn't normally want to print that
> as integer value anyway.

It's not the only one (not even close).

Never assume what's "normal", especially not when designing a standard.

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [prev] | [next] | [standalone]


#632

FromKeith Thompson <kst-u@mib.org>
Date2011-07-07 16:46 -0700
Message-ID<lnhb6xzmbf.fsf@nuthaus.mib.org>
In reply to#629
"Greg A. Woods" <woods@robohack.org> writes:
> Keith Thompson <kst-u@mib.org> writes:
>> (I suppose PRI32d could be defined as "ld" even if int32_t is a typedef
>> for int, but only if "%ld" actually works with an int32_t or int
>> argument.)
>
> Exactly, and thus a compiler that cares about portability must then
> complain that format of "%ld" does not match type of "int", and
> rightfully so.

Compilers are never required to complain about printf formats.

On the other hand, compilers are *allowed* to warn about anything
they like.

Presumably an implementation that warns about mismatched printf
formats, even those that happen to work (say, because int and long
are the same size) would define the PRI* macros so they avoid those
warnings when used correctly.  For that matter, there's no good
reason for an implementation *not* to use matching definitions for
the PRI* macros; if int32_t is typedef'ed as int, then PRId32 will
probably be defined as "d", even if "ld" would also work.

I suggest that, at least in this specific case, you're looking for
problems that are unlikely to exist in any real implementation.

> You can't escape the casts with the PRI* macros even if you're just
> trying to play with ints and longs.

If you're printing ints and longs, you don't need to use the PRI*
macros.  Just use "%d" and "%ld", respectively.  Or did I misunderstand
your point?

> (Maybe the standard should be fixed in this respect, but it wouldn't
> really solve enough of the problems with the PRI* debacle.)
>
>
>> The language and library don't make it *easy* to print raw time_t
>> values because there's rarely a good reason to do so.  That's what
>> strftime() is for.
>
> It might be rare in your world, but it's far from rare in my world.
>
> Also, I repeat, C99 strftime() is unable to print the value of a time_t
> as an integer representation of seconds since the epoch of time_t.  (C99
> does not have the '%s' specifier that many implementations do have.)

That's because the language doesn't say that time_t represents an
integer number of seconds since some epoch.  It says only that it's
an arithmetic type "capable of representing times".  Among many
other possibilities, it could use the value 20110707233829, or
0x20110707233829, or 020110707233829, to represent the current time.
Or the resolution could be larger or smaller than one second.
The relationship between time_t values and real times needn't even
be linear.

No, the PRI* macros do not solve the problem of printing raw time_t
values; they're not intended to.  I do sometimes print such values
myself; I generally just write something like

    printf("%ld\n", (long)time(NULL));

which isn't entirely portable, but it's good enough for most purposes.

>> The only library-defined *integer* type that isn't covered, as far
>> as I can tell, is wchar_t.  You wouldn't normally want to print that
>> as integer value anyway.
>
> It's not the only one (not even close).

So what are the others?

> Never assume what's "normal", especially not when designing a standard.

Don't assume that the PRI* macros are intended to solve the problem
of printing values of any and all language-defined integer types.
They're not.  Since it's straightforward to print any integer value
by converting it to intmax_t or uintmax_t, I don't see that this
is much of a problem.

Personally, I don't think I've ever used the PRI* macros.  I prefer
to convert to intmax_t or uintmax_t, or to long or unsigned long if
I'm using a non-C99 implementation or if I'm printing a type that
I happen to know is no wider than long.  That doesn't mean I don't
thing the PRI* macros should exist; there are plenty of language
and library features I don't use myself.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#633

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-07 18:26 -0700
Message-ID<87zkkpef5z.fsf@once.weird.com>
In reply to#632
Keith Thompson <kst-u@mib.org> writes:
> 
> Presumably an implementation that warns about mismatched printf
> formats, even those that happen to work (say, because int and long
> are the same size) would define the PRI* macros so they avoid those
> warnings when used correctly.

"when used correctly"?  Just how is one to do that?  Without always
using a cast, that is?

BTW, if one does always use a cast, then one can always use the exact
right cast for "%d" or "%ld" or "%u" or "%lu", etc., and thus the PRI*
macros once again prove useless and futile.

> I suggest that, at least in this specific case, you're looking for
> problems that are unlikely to exist in any real implementation.

You mean like in some versions GNU/Linux, or more specifically, I think,
some(all?) versions of glibc where PRId32 is "%ld" even on 32-bit
systems with 32-bit ints and where int32_t is a "signed int"?  At least
as far as I can tell. (I don't generally use GNU/Linux, and I despise
glibc for many other reasons than this :-))


> If you're printing ints and longs, you don't need to use the PRI*
> macros.  Just use "%d" and "%ld", respectively.  Or did I misunderstand
> your point?

That is sort of my point.  The tricky part comes when one wants to print
something like time_t (e.g. knowing that it is an integer in 99.999% of
host environments where one's code can ever be used), then one still has
to decide whether to use "%ld" and cast to a "long", or to use "%lld"
and cast to "long long", or to just give up on performance and always
use "%jd" and cast to "intmax_t".

PRI* are in fact being misused in real-world code, and to the best of my
understanding most people cannot make valid use of them -- i.e. they are
harmful to the goal of making code more portable.  It's hard enough
getting junoir programmers to use casts properly in C, and they still
trip up some of us after dozens of years of using them.

Thus my suggestion of enhancing printf() instead of trying to suggest
that the standard provide more PRI* like macros.  Let's get rid of the
problem entirely, or at least provide a truely safe and innovative
solution for those who would prefer a better way.

Once upon a time C expected the programmer to conscientiously use the
proper types for every function call without anything more than separate
tools like "lint" to sometimes help check for errors.  Now the standard
now offers prototypes and compiler writers have made it possible for
compiles to fail if the programmer makes a mistake with writing
prototypes.  We can do somethin similar for printf() (and scanf()) by
simply making sure that the programmer can always specify exactly what's
being passed as an integer (pointer) value, and we can do it without
changing the fundamental language in any incompatible way.


>> Also, I repeat, C99 strftime() is unable to print the value of a time_t
>> as an integer representation of seconds since the epoch of time_t.  (C99
>> does not have the '%s' specifier that many implementations do have.)
>
> That's because the language doesn't say that time_t represents an
> integer number of seconds since some epoch.  It says only that it's
> an arithmetic type "capable of representing times".  Among many
> other possibilities, it could use the value 20110707233829, or
> 0x20110707233829, or 020110707233829, to represent the current time.
> Or the resolution could be larger or smaller than one second.
> The relationship between time_t values and real times needn't even
> be linear.

I think you missed my point, _entirely_.  C99 strftime() cannot EVER do
what I want, no matter how time_t is defined.  I.e. strftime() IS NOT
the answer -- not even close, not even for just time_t.  Get off your
high horse about time_t!  :-)   It's an example, not the whole problem.


> No, the PRI* macros do not solve the problem of printing raw time_t
> values; they're not intended to.  I do sometimes print such values
> myself; I generally just write something like
>
>     printf("%ld\n", (long)time(NULL));
>
> which isn't entirely portable, but it's good enough for most purposes.

Every bit of code like that will break on POSIX systems in 2038.  :-)
Even on the systems which update time_t to be more than 32 bits.


>>> The only library-defined *integer* type that isn't covered, as far
>>> as I can tell, is wchar_t.  You wouldn't normally want to print that
>>> as integer value anyway.
>>
>> It's not the only one (not even close).
>
> So what are the others?

UTSL!

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [prev] | [next] | [standalone]


#635

FromJames Kuyper <jameskuyper@verizon.net>
Date2011-07-07 22:35 -0400
Message-ID<iv5qd4$f26$1@dont-email.me>
In reply to#633
On 07/07/2011 09:26 PM, Greg A. Woods wrote:
> Keith Thompson <kst-u@mib.org> writes:
>>
>> Presumably an implementation that warns about mismatched printf
>> formats, even those that happen to work (say, because int and long
>> are the same size) would define the PRI* macros so they avoid those
>> warnings when used correctly.
> 
> "when used correctly"?  Just how is one to do that?  Without always
> using a cast, that is?

The right way to warn about mismatches: only warn when there's an actual
mismatch.

The right way to #define PRI32d: #define it to be a format specification
which is NOT a mismatch for the corresponding promoted type; no cast
required.

The right way to use PRI32d (no cast required):
	int32_t value;
	// set value
	printf("value:%" PRI32d "\n", value);

If this produces the wrong results, the implementation is
non-conforming. If it results in a diagnostic, that's just poor QoI; as
such it's outside the domain of the standard. Either way, you should
complain to the vendor.

> BTW, if one does always use a cast, then one can always use the exact
> right cast for "%d" or "%ld" or "%u" or "%lu", etc., and thus the PRI*
> macros once again prove useless and futile.

They are not useless, though they are excessively clumsy. They allow you
avoid pointlessly converting to a type that's much bigger than is
actually needed, just because that's the only type you can portably
assume is big enough.

> Thus my suggestion of enhancing printf() instead of trying to suggest
> that the standard provide more PRI* like macros.  Let's get rid of the
> problem entirely, or at least provide a truely safe and innovative
> solution for those who would prefer a better way.

An implementation that can't be bothered to define the PRI* macros
correctly cannot be trusted to implement your proposed printf()
enhancements, either.

>>> Also, I repeat, C99 strftime() is unable to print the value of a time_t
>>> as an integer representation of seconds since the epoch of time_t. ...

Well, of course not. For that you use
	printf("seconds since epoch:%.0f\n", difftime(value,epoch));
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#638

From"Greg A. Woods" <woods@robohack.org>
Date2011-07-07 20:58 -0700
Message-ID<87mxgpe84b.fsf@once.weird.com>
In reply to#635
James Kuyper <jameskuyper@verizon.net> writes:
> 
> The right way to warn about mismatches: only warn when there's an actual
> mismatch.

It is an "actual" mis-match -- at least if you intend your code to be
portable.

> The right way to #define PRI32d: #define it to be a format specification
> which is NOT a mismatch for the corresponding promoted type; no cast
> required.

Someone should try this on some GNU/Linux variants for me -- I think
from my manual inspection of glibc header sources online that you'll get
a warning (if you turn on the right warning flags)....
It works OK on NetBSD and on OS X.

> The right way to use PRI32d (no cast required):
> 	int32_t value;
> 	// set value
> 	printf("value:%" PRI32d "\n", value);

Some code does that, but most folks don't seem to be PL/1-heads,
i.e. that pedantic about how many bits they want.  Most folks want an
"int", or maybe a "long" for bigger values.  They can't be bothered to
worry about numbers of bits.


> An implementation that can't be bothered to define the PRI* macros
> correctly cannot be trusted to implement your proposed printf()
> enhancements, either.

Sigh....


>>>> Also, I repeat, C99 strftime() is unable to print the value of a time_t
>>>> as an integer representation of seconds since the epoch of time_t. ...
>
> Well, of course not. For that you use
> 	printf("seconds since epoch:%.0f\n", difftime(value,epoch));

Decades of unix tradition flushed down the drain by C99.  :-)

-- 
						Greg A. Woods
						RoboHack

<woods@robohack.ca>     living on the edge      http://www.robohack.ca/

[toc] | [prev] | [next] | [standalone]


#640

FromWojtek Lerch <wojtek_l@yahoo.ca>
Date2011-07-08 00:28 -0400
Message-ID<97nfa9FgtuU1@mid.individual.net>
In reply to#638
On 07/07/2011 11:58 PM, Greg A. Woods wrote:
> James Kuyper<jameskuyper@verizon.net>  writes:
...
>> The right way to use PRI32d (no cast required):
>> 	int32_t value;
>> 	// set value
>> 	printf("value:%" PRI32d "\n", value);
>
> Some code does that, but most folks don't seem to be PL/1-heads,
> i.e. that pedantic about how many bits they want.  Most folks want an
> "int", or maybe a "long" for bigger values.  They can't be bothered to
> worry about numbers of bits.

In code that deals with binary data exchanged between different programs 
(via files, network, or whatever), using uint32_t and friends in a 
struct is a practical way of increasing portability between compilers 
that have different sizes of the standard types.  If you deal with data 
in that kind of structures, it's natural to use the same types even when 
you copy scalars into local variables or do math on them.  (Arguably 
uint_fast32_t etc would be a better choice, but their names are just so 
darn long and ugly that everybody I know, including myself, tends to 
ignore them.)

...
>>>>> Also, I repeat, C99 strftime() is unable to print the value of a time_t
>>>>> as an integer representation of seconds since the epoch of time_t. ...
>>
>> Well, of course not. For that you use
>> 	printf("seconds since epoch:%.0f\n", difftime(value,epoch));
>
> Decades of unix tradition flushed down the drain by C99.  :-)

There's a lot of traditional Unix functionality that C99 chooses not to 
support (such as directories, processes, file permissions, etc).  All 
that stuff, along with integer time_t that linearly counts seconds, 
exists in POSIX -- and C99 doesn't forbid you to rely on POSIX if you 
don't mind losing some portability.

[toc] | [prev] | [next] | [standalone]


Page 1 of 4  [1] 2 3 4  Next page →

Back to top | Article view | comp.std.c


csiph-web