Re: [RFC] convert a host address to a string

Joel Brobecker
2009-01-08T10:19:30Z

> Probably not, but there's a problem with %p. While it is specified by
> C90 and almost certainly implemented in the C library of all systems
> we care about, it is implemented how exactly the pointer will be
> printed. On OpenBSD and Linux it is something like 0xNNNNNNNN, but
> Solaris generates NNNNNNNN (without the initial 0x). That's
> undesirable I think.

I agree.

> An option would be to use the strategy used by phex_nz() to print host
> addresses. Or we could use PRINTF_HAS_LONG_LONG, and always use %llx
> if it's available.

Unfortunately, I don't know how this could be made to work.
The problem is that GCC insists that the integer type that we
use to cast the host address to must have the same size.
At one point, hoping that GCC would kill the wrong branch,
I even tried:

if (sizeof (void *) == sizeof (long))
printf ("0x%lx", (long) address);
else
printf ("0x%llx", (long long) address);

But this didn't work, because GCC complained about the cast
in the "if" branch.

Actually, it's only after writing the entire email that I realized
that we have another option. See option (3) below.

> I'd really like to avoid introducing another macro dealing with
> type-size issues if possible. I especially dislike HOST_IS_LLP64
> since I fear its existence encourages people to write unportable code.

I can see several solutions:

1. Use %p. To overcome the problem with 0x, we could use
two alternatives:

a. Import printf from gnulib. I looked at this a while ago,
for some other issue, and I immediately stopped, as it
looked like it might be a lot of work to do so (printf
doesn't come alone, there's a bunch of other routines
that printf uses which we probably want).

b. Strip the leading "0x" if %p already provides it. In other
words:

fprintf (buf, "0x%p", address);
if (buf[2] == '0' && buf[3] == 'x')
buf = buf + 2;
return buf;

There is no memory management issue in this case, because
the buffer we return is more or less static. It's part
of a bunch of buffers we cycle through each time we call
this routine. The caller never frees the memory we return.

2. Avoid the HOST_IS_LLP64 macro, but still do something similar
inside host_address_to_string. Something like:

#if defined(WIN64_)
fprintf (buf, "0x%llx", (unsigned long long) address);
#else
fprintf (buf, "0x%lx", (unsigned long) address);
#endif

This eliminates the likeliness of re-using the HOST_IS_LLP64
macro to write non-portable code.

3. Work through uintptr_t.

#ifdef PRINTF_HAS_LONG_LONG
fprintf (buf, "0x%llx", (unsigned long long) (uintptr_t) address);
#else
fprintf (buf, "0x%lx", (unsigned long) (uintptr_t) address);
#endif

For completeness' sake, I also investigate the use of the PRIxPTR
macro, but we still have the problem of casting the address to
the right integer type: If PRIxPTR resolves to "lx", then we should
cast to "long", otherwise, we should cas to "long long".

I kinda like option 1b as being simple and avoiding the need to
cast the address to an integer. Option (3) is my next favorite,
but I don't like the fact that we end up doing an unnecessary
integer promotion on the 32bit targets. Perhaps we could avoid that
using an extra "if (sizeof (void *) != sizeof (long))" but then
the code becomes increasingly complex. My next favorite would
probably be option 2 because I'm lazy, but it's really not elegant.
Option 1 looks like a fair amount of work, but would give us access
to a predicatable printf.

Thoughts?

--
Joel