/**
** copyright © 2014, Jens Gustedt
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
**
** 2. Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
** CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
** ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
** TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
** THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**/
/**
** @file
** @brief Test C standard numerical macros for their size and type
**
** The C standard imposes types for a lot of numerical values, such
** as minimum and maximum values of certain types.
**
** Before C11, only the size and sometimes the signedness of a
** integer or floating point literal have been observable. With C11
** the type itself becomes observable through _Generic
** expressions. Portability requires then that the types of the
** macros are exactly what the standard defines.
**
** There are basically three forms of restrictions by the
** standard.
**
** The first is an exact definition of integer constants, e.g
** UINT16_C(1)
must be of type
** uint_least16_t
. These must all be suitable for
** evaluation in the preprocessor, so we also test them for that.
**
** The second form is a prescription of the promoted type. These are
** e.g the minimum and maximum values of signed integer types. So the
** promotion rules apply for them: for narrow types the resulting
** type for the constant is int (most of the times) for wide types it
** that same wide types. E.g SHORT_MAX
is usually of
** type int
, LONG_MAX
is long
.
** They also must be suitable for evaluation in the preprocessor, so
** we also test them for that.
**
** The third is an exact definition of floating point constants, e.g
** _Complex_I
must be of type _Complex
** double
. These can't be used in the preprocessor, so we
** don't test that, but must be suitable in initializers of static
** variables. So we test for that.
**
** This program implements tests on two different levels. The first
** should work with any C99 compatible compiler. It checks that the
** sizes are correct, for signedness when that is possible, that is
** when the type doesn't promote, and if the constant is suitable for
** initialization of static objects.
**
** When compiled with a C11 compiler and additional check for the
** type is made. The program prints all findings and a summary.
**
** @return is @c EXIT_SUCCESS if all sizes and types were
** correct. Otherwise @c EXIT_FAILURE is returned.
**/
#ifndef __STDC_NO_COMPLEX__
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* some utilities to use with _Generic for type identification */
enum cls {
c_def,
c_b,
c_c,
c_hhi,
c_hhu,
c_hi,
c_hu,
c_i,
c_u,
c_li,
c_lu,
c_lli,
c_llu,
c_f,
c_lf,
c_llf,
c_cf,
c_clf,
c_cllf,
};
#ifndef __STDC_NO_COMPLEX__
# define COMPLEX_TYPES _Complex float: c_cf, _Complex double: c_clf, _Complex long double: c_cllf
#else
# define COMPLEX_TYPES
#endif
#define TYPEID(X) \
_Generic((X), \
default: c_def, \
_Bool: c_b, \
char: c_c, signed char: c_hhi, unsigned char: c_hhu, \
signed short: c_hi, unsigned short: c_hu, \
signed: c_i, unsigned: c_u, \
signed long: c_li, unsigned long: c_lu, \
signed long long: c_lli, unsigned long long: c_llu, \
float: c_f, double: c_lf, long double: c_llf, \
COMPLEX_TYPES)
#define TYPENAME(X) \
(char const*[]){ \
[c_def] = "", \
[c_b] = "_Bool", \
[c_c] = "char", \
[c_hhi] = "signed char", \
[c_hhu] = "unsigned char", \
[c_hi] = "signed short int", \
[c_hu] = "unsigned short int", \
[c_i] = "signed int", \
[c_u] = "unsigned int", \
[c_li] = "signed long int", \
[c_lu] = "unsigned long int", \
[c_lli] = "signed long long int", \
[c_llu] = "unsigned long long int", \
[c_f] = "float", \
[c_lf] = "double", \
[c_llf] = "long double", \
[c_cf] = "_Complex float", \
[c_clf] = "_Complex double", \
[c_cllf] = "_Complex long double", \
}[(X)]
/* Counters to track the number of bugs. */
static size_t size_right;
static size_t size_wrong;
static size_t sign_right;
static size_t sign_wrong;
static size_t type_right;
static size_t type_wrong;
static size_t missing;
#define CHECK_EXACT_SIZE(T, X, ST, SX) \
do { \
if (sizeof(X) == sizeof(T)) { \
++size_right; \
printf("%20s, %20s:\tright size\n", SX, ST); \
} else { \
++size_wrong; \
printf("%20s, %20s:\twrong size, %s, %zu != %zu\n", \
SX, ST, #X, sizeof(X), sizeof(T)); \
} \
} while(0)
#define CHECK_EXACT_TYPE(T, X, ST, SX) \
do { \
int type_x = TYPEID(X); \
int type_unpromoted = TYPEID((T)0); \
if (_Generic((X), T: 1, default: 0)) { \
++type_right; \
printf("%20s, %20s:\tright type \"%s\"\n", SX, ST, \
TYPENAME(type_x)); \
} else { \
++type_wrong; \
printf("%20s, %20s:\twrong type \"%s\", should be \"%s\"\n", \
SX, ST, TYPENAME(type_x), TYPENAME(type_unpromoted)); \
} \
} while(0)
#define CHECK_SIGN(T, X, ST, SX) \
do { \
_Bool ispromoted = (sizeof(T) != sizeof(+(T)0)); \
if (!ispromoted) { \
_Bool issinged_T = (0 > (T)-1); \
_Bool issinged_x = (0 > (1 ? -1 : (X))); \
if (issinged_x == issinged_T) { \
++sign_right; \
} else { \
++sign_wrong; \
printf("%20s, %20s:\twrong sign, %ssigned instead of %ssigned\n", \
SX, ST, \
(issinged_x ? "" : "un"), \
(issinged_T ? "" : "un")); \
} \
} \
} while(0)
#if __STDC_VERSION__ > 201100L
# define CHECK_EXACT(T, X) \
do { \
CHECK_EXACT_SIZE(T, X, #T, #X); \
CHECK_SIGN(T, X, #T, #X); \
CHECK_EXACT_TYPE(T, X, #T, #X); \
} while (0)
#else
# define CHECK_EXACT(T, X) \
do { \
CHECK_EXACT_SIZE(T, X, #T, #X); \
CHECK_SIGN(T, X, #T, #X); \
} while (0)
#endif
#define CHECK_CONSTANT(T, X, ST, SX) \
do { \
static T volatile const obj = X; \
(void)obj; \
} while (0)
#if __STDC_VERSION__ > 201100L
# define CHECK_UNORDERED(T, X) \
do { \
CHECK_CONSTANT(T, X, #T, #X); \
CHECK_EXACT_SIZE(T, X, #T, #X); \
CHECK_EXACT_TYPE(T, X, #T, #X); \
} while (0)
#else
# define CHECK_UNORDERED(T, X) \
do { \
CHECK_CONSTANT(T, X, #T, #X); \
CHECK_EXACT_SIZE(T, X, #T, #X); \
} while (0)
#endif
#define CHECK_PROMOTED_SIZE(T, X, ST, SX) \
do { \
if (sizeof(X) == sizeof(+(T)0)) { \
++size_right; \
printf("%20s, %20s:\tright size\n", SX, ST); \
} else { \
++size_wrong; \
printf("%20s, %20s:\twrong size, %s, %zu != %zu\n", \
SX, ST, #X, sizeof(X), sizeof(+(T)0)); \
} \
} while(0)
#define CHECK_PROMOTED_TYPE(T, X, ST, SX) \
do { \
bool unpromoted = _Generic(+(T)0, T: true, default: false); \
int type_x = TYPEID(X); \
int type_promoted = TYPEID(+(T)0); \
int type_unpromoted = TYPEID((T)0); \
bool correct = (unpromoted \
/* an unpromoted type must match */ \
? _Generic((X), T: 1, default: 0) \
/* an promoted type mustn't */ \
: type_x == type_promoted); \
if (correct) { \
++type_right; \
printf("%20s, %20s:\tright %spromoted type \"%s\"\n", SX, ST, \
(unpromoted ? "un" : ""), \
TYPENAME(type_promoted)); \
} else { \
++type_wrong; \
printf("%20s, %20s:\twrong type, has type \"%s\" should be %spromoted type \"%s\"\n", \
SX, ST, TYPENAME(type_x), \
(unpromoted ? "un" : ""), \
(unpromoted \
? TYPENAME(type_unpromoted) \
: TYPENAME(type_promoted))); \
} \
} while(0)
#if __STDC_VERSION__ > 201100L
# define CHECK_PROMOTED(T, X) \
do { \
CHECK_PROMOTED_SIZE(T, X, #T, #X); \
CHECK_SIGN(T, X, #T, #X); \
CHECK_PROMOTED_TYPE(T, X, #T, #X); \
} while (0)
#else
# define CHECK_PROMOTED(T, X) \
do { \
CHECK_PROMOTED_SIZE(T, X, #T, #X); \
CHECK_SIGN(T, X, #T, #X); \
} while (0)
#endif
int main(void) {
/* types that must be unsigned */
#if false+1
CHECK_PROMOTED(bool, false);
#endif
#if true+1
CHECK_PROMOTED(bool, true);
#endif
#if UINT8_C(1) > 0
CHECK_EXACT(uint_least8_t, UINT8_C(1));
#endif
#if UINT16_C(1) > 0
CHECK_EXACT(uint_least16_t, UINT16_C(1));
#endif
#if UINT32_C(1) > 0
CHECK_EXACT(uint_least32_t, UINT32_C(1));
#endif
#if UINT64_C(1) > 0
CHECK_EXACT(uint_least64_t, UINT64_C(1));
#endif
#if UCHAR_MAX > 0
CHECK_PROMOTED(unsigned char, UCHAR_MAX);
#endif
#if USHRT_MAX > 0
CHECK_PROMOTED(unsigned short, USHRT_MAX);
#endif
#if UINT_MAX > 0
CHECK_EXACT(unsigned int, UINT_MAX);
#endif
#if ULONG_MAX > 0
CHECK_EXACT(unsigned long, ULONG_MAX);
#endif
#if ULLONG_MAX > 0
CHECK_EXACT(unsigned long long, ULLONG_MAX);
#endif
#if UINT8_MAX > 0
CHECK_PROMOTED(uint8_t, UINT8_MAX);
#endif
#if UINT16_MAX > 0
CHECK_PROMOTED(uint16_t, UINT16_MAX);
#endif
#if UINT32_MAX > 0
CHECK_PROMOTED(uint32_t, UINT32_MAX);
#endif
#if UINT64_MAX > 0
CHECK_PROMOTED(uint64_t, UINT64_MAX);
#endif
#if UINT_LEAST8_MAX > 0
CHECK_PROMOTED(uint_least8_t, UINT_LEAST8_MAX);
#endif
#if UINT_LEAST16_MAX > 0
CHECK_PROMOTED(uint_least16_t, UINT_LEAST16_MAX);
#endif
#if UINT_LEAST32_MAX > 0
CHECK_PROMOTED(uint_least32_t, UINT_LEAST32_MAX);
#endif
#if UINT_LEAST64_MAX > 0
CHECK_PROMOTED(uint_least64_t, UINT_LEAST64_MAX);
#endif
#if UINT_FAST8_MAX > 0
CHECK_PROMOTED(uint_fast8_t, UINT_FAST8_MAX);
#endif
#if UINT_FAST16_MAX > 0
CHECK_PROMOTED(uint_fast16_t, UINT_FAST16_MAX);
#endif
#if UINT_FAST32_MAX > 0
CHECK_PROMOTED(uint_fast32_t, UINT_FAST32_MAX);
#endif
#if UINT_FAST64_MAX > 0
CHECK_PROMOTED(uint_fast64_t, UINT_FAST64_MAX);
#endif
#if UINTMAX_MAX > 0
CHECK_EXACT(uintmax_t, UINTMAX_MAX);
#endif
#if UINPTR_MAX > 0
CHECK_PROMOTED(uintptr_t, UINTPTR_MAX);
#endif
#if SIZE_MAX > 0
CHECK_PROMOTED(size_t, SIZE_MAX);
#endif
/* types that that maybe signed */
#if INT8_C(1) > 0
CHECK_EXACT(int_least8_t, INT8_C(1));
#endif
#if INT16_C(1) > 0
CHECK_EXACT(int_least16_t, INT16_C(1));
#endif
#if INT32_C(1) > 0
CHECK_EXACT(int_least32_t, INT32_C(1));
#endif
#if INT64_C(1) > 0
CHECK_EXACT(int_least64_t, INT64_C(1));
#endif
#if CHAR_MAX > 0
CHECK_PROMOTED(char, CHAR_MAX);
#endif
#if SCHAR_MAX > 0
CHECK_PROMOTED(signed char, SCHAR_MAX);
#endif
#if SHRT_MAX > 0
CHECK_PROMOTED(signed short, SHRT_MAX);
#endif
#if INT_MAX > 0
CHECK_EXACT(signed int, INT_MAX);
#endif
#if LONG_MAX > 0
CHECK_EXACT(signed long, LONG_MAX);
#endif
#if LLONG_MAX > 0
CHECK_EXACT(signed long long, LLONG_MAX);
#endif
#if INT8_MAX > 0
CHECK_PROMOTED(int8_t, INT8_MAX);
#endif
#if INT16_MAX > 0
CHECK_PROMOTED(int16_t, INT16_MAX);
#endif
#if INT32_MAX > 0
CHECK_PROMOTED(int32_t, INT32_MAX);
#endif
#if INT64_MAX > 0
CHECK_PROMOTED(int64_t, INT64_MAX);
#endif
#if INT_LEAST8_MAX > 0
CHECK_PROMOTED(int_least8_t, INT_LEAST8_MAX);
#endif
#if INT_LEAST16_MAX > 0
CHECK_PROMOTED(int_least16_t, INT_LEAST16_MAX);
#endif
#if INT_LEAST32_MAX > 0
CHECK_PROMOTED(int_least32_t, INT_LEAST32_MAX);
#endif
#if INT_LEAST64_MAX > 0
CHECK_PROMOTED(int_least64_t, INT_LEAST64_MAX);
#endif
#if INT_FAST8_MAX > 0
CHECK_PROMOTED(int_fast8_t, INT_FAST8_MAX);
#endif
#if INT_FAST16_MAX > 0
CHECK_PROMOTED(int_fast16_t, INT_FAST16_MAX);
#endif
#if INT_FAST32_MAX > 0
CHECK_PROMOTED(int_fast32_t, INT_FAST32_MAX);
#endif
#if INT_FAST64_MAX > 0
CHECK_PROMOTED(int_fast64_t, INT_FAST64_MAX);
#endif
#if INTMAX_MAX > 0
CHECK_PROMOTED(intmax_t, INTMAX_MAX);
#endif
#if UINPTR_MAX > 0
CHECK_PROMOTED(intptr_t, INTPTR_MAX);
#endif
#if PTRDIFF_MAX > 0
CHECK_PROMOTED(ptrdiff_t, PTRDIFF_MAX);
#endif
#if SIG_ATOMIC_MAX > 0
CHECK_PROMOTED(sig_atomic_t, SIG_ATOMIC_MAX);
#endif
#if WCHAR_MAX > 0
CHECK_PROMOTED(wchar_t, WCHAR_MAX);
#endif
#if WINT_MAX > 0
/* wint_t should not promote */
CHECK_EXACT(wint_t, WINT_MAX);
#endif
#if CHAR_MIN < 1
CHECK_PROMOTED(char, CHAR_MIN);
#endif
#if SCHAR_MIN < 1
CHECK_PROMOTED(signed char, SCHAR_MIN);
#endif
#if SHRT_MIN < 1
CHECK_PROMOTED(signed short, SHRT_MIN);
#endif
#if INT_MIN < 1
CHECK_EXACT(signed int, INT_MIN);
#endif
#if LONG_MIN < 1
CHECK_EXACT(signed long, LONG_MIN);
#endif
#if LLONG_MIN < 1
CHECK_EXACT(signed long long, LLONG_MIN);
#endif
#if INT8_MIN < 1
CHECK_PROMOTED(int8_t, INT8_MIN);
#endif
#if INT16_MIN < 1
CHECK_PROMOTED(int16_t, INT16_MIN);
#endif
#if INT32_MIN < 1
CHECK_PROMOTED(int32_t, INT32_MIN);
#endif
#if INT64_MIN < 1
CHECK_PROMOTED(int64_t, INT64_MIN);
#endif
#if INT_LEAST8_MIN < 1
CHECK_PROMOTED(int_least8_t, INT_LEAST8_MIN);
#endif
#if INT_LEAST16_MIN < 1
CHECK_PROMOTED(int_least16_t, INT_LEAST16_MIN);
#endif
#if INT_LEAST32_MIN < 1
CHECK_PROMOTED(int_least32_t, INT_LEAST32_MIN);
#endif
#if INT_LEAST64_MIN < 1
CHECK_PROMOTED(int_least64_t, INT_LEAST64_MIN);
#endif
#if INT_FAST8_MIN < 1
CHECK_PROMOTED(int_fast8_t, INT_FAST8_MIN);
#endif
#if INT_FAST16_MIN < 1
CHECK_PROMOTED(int_fast16_t, INT_FAST16_MIN);
#endif
#if INT_FAST32_MIN < 1
CHECK_PROMOTED(int_fast32_t, INT_FAST32_MIN);
#endif
#if INT_FAST64_MIN < 1
CHECK_PROMOTED(int_fast64_t, INT_FAST64_MIN);
#endif
#if INTMAX_MIN < 1
CHECK_PROMOTED(intmax_t, INTMAX_MIN);
#endif
#if UINPTR_MIN < 1
CHECK_PROMOTED(intptr_t, INTPTR_MIN);
#endif
#if PTRDIFF_MIN < 1
CHECK_PROMOTED(ptrdiff_t, PTRDIFF_MIN);
#endif
#if SIG_ATOMIC_MIN < 1
CHECK_PROMOTED(sig_atomic_t, SIG_ATOMIC_MIN);
#endif
#if WCHAR_MIN < 1
CHECK_PROMOTED(wchar_t, WCHAR_MIN);
#endif
#if EOF < 1 || EOF > 0
CHECK_EXACT(int, EOF);
#endif
#if WINT_MIN < 1
/* wint_t should not promote */
CHECK_EXACT(wint_t, WINT_MIN);
#endif
#if WEOF < 1 || WEOF > 0
/* wint_t should not promote */
CHECK_EXACT(wint_t, WEOF);
#endif
CHECK_UNORDERED(float, FLT_MAX);
CHECK_UNORDERED(float, FLT_MIN);
CHECK_UNORDERED(float, FLT_TRUE_MIN);
CHECK_UNORDERED(float, FLT_EPSILON);
CHECK_UNORDERED(float, HUGE_VALF);
CHECK_UNORDERED(float, INFINITY);
#ifdef NAN
CHECK_UNORDERED(float, NAN);
#endif
CHECK_UNORDERED(double, DBL_MAX);
CHECK_UNORDERED(double, DBL_MIN);
CHECK_UNORDERED(double, DBL_TRUE_MIN);
CHECK_UNORDERED(double, DBL_EPSILON);
CHECK_UNORDERED(double, HUGE_VAL);
CHECK_UNORDERED(long double, LDBL_MAX);
CHECK_UNORDERED(long double, LDBL_MIN);
CHECK_UNORDERED(long double, LDBL_TRUE_MIN);
CHECK_UNORDERED(long double, LDBL_EPSILON);
CHECK_UNORDERED(long double, HUGE_VALL);
#ifndef __STDC_NO_COMPLEX__
# ifdef imaginary
CHECK_UNORDERED(_Imaginary float, I);
# else
CHECK_UNORDERED(_Complex float, I);
# endif
/* Notwithstanding the provisions of 7.1.3, a program may undefine
and perhaps then redefine the macros complex, imaginary, and
I. */
# undef imaginary
# undef complex
# undef I
/* Try to invent the most stupid choices that an application could
ever make. */
# define imaginary long double
# define complex long double
# define I 1.4L
/* Now all other macros should still work. */
# ifdef _Imaginary_I
CHECK_UNORDERED(_Imaginary float, _Imaginary_I);
# endif
CHECK_UNORDERED(_Complex float, _Complex_I);
CHECK_UNORDERED(_Complex float, 1.0F + _Complex_I);
CHECK_UNORDERED(_Complex double, 1.0 + _Complex_I);
CHECK_UNORDERED(_Complex long double, 1.0L + _Complex_I);
# ifdef CMPLXF
CHECK_UNORDERED(_Complex float, CMPLXF(1.0L, 1.0L));
# else
++missing;
puts("macro CMPLXF is missing");
# endif
# ifdef CMPLX
CHECK_UNORDERED(_Complex double, CMPLX(1.0L, 1.0L));
# else
++missing;
puts("macro CMPLX is missing");
# endif
# ifdef CMPLXL
CHECK_UNORDERED(_Complex long double, CMPLXL(1.0F, 1.0F));
# else
++missing;
puts("macro CMPLXL is missing");
# endif
#endif
printf("\nSummary:\t%zu/%zu sizes wrong\n", size_wrong, size_wrong+size_right);
printf("\t\t%zu/%zu signedness wrong\n", sign_wrong, sign_wrong+sign_right);
#if __STDC_VERSION__ > 201100L
printf("\t\t%zu/%zu types wrong\n", type_wrong, type_wrong+type_right);
#endif
printf("\t\t%zu macros are missing\n", missing);
puts("\ncheck a set of literals that all should work, independent of the C library");
CHECK_EXACT(int, 'a');
CHECK_EXACT(wchar_t, L'a');
#if __STDC_VERSION__ > 201100L
CHECK_EXACT(uint_least16_t, u'a');
CHECK_EXACT(uint_least32_t, U'A');
#endif
#ifdef __SIZEOF_INT128__
CHECK_EXACT(__int128, (__int128)0);
#endif
CHECK_UNORDERED(float, 0.0F);
CHECK_UNORDERED(double, 0.0);
CHECK_UNORDERED(long double, 0.0L);
int ret = type_wrong + size_wrong + sign_wrong + missing;
puts("\ncheck a set of mismatches that should error, independent of the C library");
CHECK_UNORDERED(double, 0LL);
CHECK_PROMOTED(double, 0LL);
#ifdef __SIZEOF_INT128__
CHECK_EXACT(__int128, 0LL);
#endif
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
}