#1 incorrect type signedness for limits.h replacement, unchecked sizes not guaranteeing portability by standard
Opened 2 years ago by matu3ba. Modified 2 years ago

problem source: "wchar_t is unsigned or signed" https://stackoverflow.com/a/11953419
"The signedness of wchar_t is unspecified. The standard only says (3.9.1/5):"
C standard further does not guarantee sizes, which you typedef. So they should be checked with macros/compiler.

From playing with the idea to write a test framework, I got the following macros to check sizes and signedness.
Unfortunately you do not write compatibility of the C standard (C11 mandates _Static_assert), so you might need to adapt them further.

For example, typedef unsigned short wchar16_t; is wrong and the user must specify it, if is necessary and for portability must _Static_assert it.
The best you can do is to provide example files, so from my point of view this should also be BSD0 licensed to make clear that stuff must be potentially modified etc.
If you have a complete list of architectures or would accept all possible architectures, that would look different. ;)

One solution to this is:

// user.h
// necessary for testing
#include <stdint.h> // abi: (u)int8_t .. (u)int64_t

// optional (see below for usage)
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE

// necessary for testing
#define assert(a) if( !( a ) )                            \
{                                                         \
    fprintf( stderr, "%s:%d assertion failure of (%s)\n", \
                             __FILE__, __LINE__, #a );    \
    exit(EXIT_FAILURE);                                            \
}                                                         \
_Static_assert(1, "") // assume: true == 1

// necessary for testing
#ifdef IS_SIGNED
#error "IS_SIGNED already defined"
#else
#define IS_SIGNED(Type) (((Type)-1) < 0)
#endif // IS_SIGNED

// optional, but recommended
#ifndef EXIT_SUCCESS
  #error "EXIT_SUCCESS undefined, usually 0"
#endif
#ifndef EXIT_FAILURE
  #error "EXIT_FAILURE undefined, usually 1"
#endif
static_assert(EXIT_SUCCESS == 0, "err: exit not 0");
static_assert(EXIT_FAILURE == 1, "err: exit not 1");
//#define EXIT_SUCCESS 0
//#define EXIT_FAILURE 1

// optional, but recommended (to adjust)
static_assert(IS_SIGNED(char),   "err: char is unsigned");
// ntalltypes.h
# include "user.h"
// all of the typedefs ...
#if defined(__NEED_wchar16_t) && !defined(__DEFINED_wchar16_t)
typedef unsigned short      wchar16_t;
#define __DEFINED_wchar16_t
#endif

typedef unsigned short      wchar16_t; // all of them

static_assert(sizeof(char) == 1, "err: char not 1 byte");

static_assert(IS_SIGNED(signed char), "err: IS_SIGNED signed char");
static_assert(sizeof(signed char) == 1, "err: char not 1 byte");
static_assert(sizeof(int8_t) == 1,  "err: int8_t not 1 byte");
static_assert(sizeof(int16_t) == 2, "err: int16_t not 2 byte");
static_assert(sizeof(int32_t) == 4, "err: int32_t not 4 byte");
static_assert(sizeof(int64_t) == 8, "err: int64_t not 8 byte");

static_assert((!IS_SIGNED(unsigned char)), "err: IS_SIGNED unsigned char");
static_assert(sizeof(unsigned char) == 1, "err: char not 1 byte");
static_assert(sizeof(uint8_t) == 1,  "err: uint8_t not 1 byte");
static_assert(sizeof(uint16_t) == 2, "err: uint16_t not 2 byte");
static_assert(sizeof(uint32_t) == 4, "err: uint32_t not 4 byte");
static_assert(sizeof(uint64_t) == 8, "err: uint64_t not 8 byte");

If you think otherwise, please add the Assumptions (unchecked things) and Assertions (checked things) to README.md.

Let me know, what you think.


One can autogenerate those definitions with grepping for
__SIZEOF_INT__ and co as explained here
https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

args =  [
            "-target",
            "aarch64-linux-gnu",
            "-E",
            // -dM is cleaner, but -dD preserves iteration order.
            "-dD",
            // No need for line-markers.
            "-P",
            "-nostdinc",
            "-Iinclude",
            "-Iinclude/uapi",
            "arch/arm64/include/uapi/asm/unistd.h",
          ] ;
clang args

__has_include("limits.h")
__has_include("stdint.h")
+ check macro definition existence

to autogenerate the static assertions for checking
if code assumptions also hold on the other platforms.
Without limits and stdint all bets are off.

This also works for signedness of char, ie with
https://exchangetuts.com/what-causes-a-char-to-be-signed-or-unsigned-when-using-gcc-1639592646188352

see also
https://github.com/ziglang/zig/pull/11447

Login to comment on this ticket.

Metadata