Discussion:
Question about portability guidelines
(too old to reply)
Paul Smith
2017-01-02 20:07:11 UTC
Permalink
Raw Message
Looking at the portability guidelines[1] there is some confusion; early
Currently we assume at least a freestanding C89 compiler
The GNU coding standards allow one departure from strict C99
Hence Gnulib code should avoid using constructs (e.g., undeclared
functions return int) that do not conform to C99.
etc. Are these references to C99 typos, and they should be C89 instead?
Gnulib code can assume that standard internal types like size_t are no
wider than long.
Is that right? On a 64bit system compiling with Microsoft Visual C,
long is only a 32bit integer. Is it saying that gnulib is specifically
not intended to work with MS Visual C?


-----
[1] https://www.gnu.org/software/gnulib/manual/html_node/Portability-guidelines.html
Bruno Haible
2017-01-02 21:17:05 UTC
Permalink
Raw Message
Post by Paul Smith
Gnulib code can assume that standard internal types like size_t are no
wider than long.
Is that right? On a 64bit system compiling with Microsoft Visual C,
long is only a 32bit integer. Is it saying that gnulib is specifically
not intended to work with MS Visual C?
You are right, on 64-bit native Windows (both mingw and MSVC), 'long' is
only 32-bit and thus smaller than 'intptr_t'.

Parts of gnulib do work on MSVC and mingw; for full coverage, there's still
a lot of work to be done [1].

I would vote for removing this sentence "Gnulib code can assume that standard
internal types like size_t are no wider than long." The fix is easy nowadays
(since MSVC now has <stdint.h>): Use intptr_t or ptrdiff_t or size_t as
appropriate.

Bruno

[1] http://lists.gnu.org/archive/html/bug-gnulib/2016-12/msg00112.html
Paul Eggert
2017-01-03 00:09:59 UTC
Permalink
Raw Message
Post by Bruno Haible
I would vote for removing this sentence "Gnulib code can assume that standard
internal types like size_t are no wider than long." The fix is easy nowadays
(since MSVC now has <stdint.h>): Use intptr_t or ptrdiff_t or size_t as
appropriate.
It's not that easy for code that wants to print such integers, as %ld doesn't
suffice on MinGW and older POSIXish libraries don't support %jd etc. Perhaps
once Gnulib assumes C99-or-later libraries, and once somebody goes through all
the Gnulib code and checks it. In the meantime we should probably leave
something like that sentence in there, although we should warn people about the
MSVC issues. I installed the attached to try to do that, and to try to clarify
the issues about C89 vs C99 vs C11 that were in Paul's question.
Ben Pfaff
2017-01-04 00:16:08 UTC
Permalink
Raw Message
Post by Paul Eggert
Post by Bruno Haible
I would vote for removing this sentence "Gnulib code can assume that standard
internal types like size_t are no wider than long." The fix is easy nowadays
(since MSVC now has <stdint.h>): Use intptr_t or ptrdiff_t or size_t as
appropriate.
It's not that easy for code that wants to print such integers, as %ld
doesn't suffice on MinGW and older POSIXish libraries don't support %jd etc.
Perhaps once Gnulib assumes C99-or-later libraries, and once somebody goes
through all the Gnulib code and checks it. In the meantime we should
probably leave something like that sentence in there, although we should
warn people about the MSVC issues. I installed the attached to try to do
that, and to try to clarify the issues about C89 vs C99 vs C11 that were in
Paul's question.
One strategy is to use PRIdPTR for ptrdiff_t and PRIdMAX or PRIuMAX
(plus a cast) for other types.

In one project of mine (which does not use Gnulib) I introduced a
PRIuSIZE macro. This is not a standard macro, but I don't understand
why not.
Paul Eggert
2017-01-04 00:33:24 UTC
Permalink
Raw Message
Post by Ben Pfaff
One strategy is to use PRIdPTR for ptrdiff_t and PRIdMAX or PRIuMAX
(plus a cast) for other types.
PRIdPTR is for intptr_t, not for ptrdiff_t.

(An aside: It's typically safer in C to assign to a typed temporary than
to cast to the type, as casts are too powerful. This is orthogonal to
the long-vs-ptrdiff_t issue.)
Post by Ben Pfaff
In one project of mine (which does not use Gnulib) I introduced a
PRIuSIZE macro. This is not a standard macro, but I don't understand
why not.
It's not in C99 because %zu is supposed to suffice, just as %td is
supposed to suffice for ptrdiff_t. Of course this does not help for
porting to pre-C99 libraries.

PRIdPTR etc. are awkward for applications that need to select the format
(d, u, x, etc.) in a portable way. For each user-defined integer
typedef, one must define a separate macro for d, for u, for x, etc. I do
not know why the C standard's authors specified PRIdPTR (e.g., "ld"),
PRIuPTR (e.g., "lu"), etc., instead of specifying just PRIPTR (e.g.,
"l") for the width part only.
Ben Pfaff
2017-01-04 17:28:11 UTC
Permalink
Raw Message
Post by Paul Eggert
(An aside: It's typically safer in C to assign to a typed temporary than
to cast to the type, as casts are too powerful. This is orthogonal to
the long-vs-ptrdiff_t issue.)
Yes.

Sometimes, to make casts safer, I declare macros to help out, like
CONST_CAST below.

For safer "casting" to intmax_t, I guess that one could just write a
function:

intmax_t to_intmax(intmax_t x) { return x; }

/* Expands to a void expression that checks that POINTER is an
expression whose type is a qualified or unqualified version of
a type compatible with TYPE (a pointer type) and, if not,
causes a compiler warning to be issued (on typical compilers).

Examples:

int *ip;
const int *cip;
const int **cipp;
int ***ippp;
double *dp;

// None of these causes a warning:
CHECK_POINTER_HAS_TYPE (ip, int *);
CHECK_POINTER_HAS_TYPE (ip, const int *);
CHECK_POINTER_HAS_TYPE (cip, int *);
CHECK_POINTER_HAS_TYPE (cip, const int *);
CHECK_POINTER_HAS_TYPE (dp, double *);
CHECK_POINTER_HAS_TYPE (dp, const double *);
CHECK_POINTER_HAS_TYPE (cipp, const int **);
CHECK_POINTER_HAS_TYPE (cipp, const int *const *);
CHECK_POINTER_HAS_TYPE (ippp, int ***);
CHECK_POINTER_HAS_TYPE (ippp, int **const *);

// None of these causes a warning either, although it is unusual to
// const-qualify a pointer like this (it's like declaring a "const int",
// for example).
CHECK_POINTER_HAS_TYPE (ip, int *const);
CHECK_POINTER_HAS_TYPE (ip, const int *const);
CHECK_POINTER_HAS_TYPE (cip, int *const);
CHECK_POINTER_HAS_TYPE (cip, const int *const);
CHECK_POINTER_HAS_TYPE (cipp, const int **const);
CHECK_POINTER_HAS_TYPE (cipp, const int *const *const);
CHECK_POINTER_HAS_TYPE (ippp, int ***const);
CHECK_POINTER_HAS_TYPE (ippp, int **const *const);

// Provokes a warning because "int" is not compatible with "double":
CHECK_POINTER_HAS_TYPE (dp, int *);

// Provoke warnings because C's type compatibility rules only allow
// adding a "const" qualifier to the outermost pointer:
CHECK_POINTER_HAS_TYPE (ippp, const int ***);
CHECK_POINTER_HAS_TYPE (ippp, int *const**);
*/
#define CHECK_POINTER_HAS_TYPE(POINTER, TYPE) \
((void) sizeof ((TYPE) (POINTER) == (POINTER)))

/* Given expressions A and B, both of which have pointer type,
expands to a void expression that causes a compiler warning if
A and B are not pointers to qualified or unqualified versions
of compatible types.

Examples similar to those given for CHECK_POINTER_HAS_TYPE,
above, can easily be devised. */
#define CHECK_POINTER_COMPATIBILITY(A, B) ((void) sizeof ((A) == (B)))

/* Equivalent to casting POINTER to TYPE, but also issues a
warning if the cast changes anything other than an outermost
"const" or "volatile" qualifier. */
#define CONST_CAST(TYPE, POINTER) \
(CHECK_POINTER_HAS_TYPE (POINTER, TYPE), \
(TYPE) (POINTER))

Loading...