I occasionally will come across an integer type (e.g. POSIX signed integer type off_t) where it would be helpful to have a macro for its minimum and maximum val
Since C11 you can use _Generic to find the underlying type. Before then __builtin_choose_expr with __typeof and __builtin_types_compatible_p was fairly portable.
If you don't want to use any of those, you could guess the type based on its size and signedness.
#include
#include
#define TP_MAX(Tp) ((Tp)-1>0 ? ( \
1==sizeof(Tp) ? ((Tp)2==1?1:UCHAR_MAX) \
: sizeof(unsigned short)==sizeof(Tp) ? USHRT_MAX \
: sizeof(unsigned int)==sizeof(Tp) ? UINT_MAX \
: sizeof(unsigned long)==sizeof(Tp) ? ULONG_MAX \
: sizeof(unsigned long long)==sizeof(Tp) ? ULLONG_MAX : 0 \
) : ( 1==sizeof(Tp) ? SCHAR_MAX \
: sizeof(short)==sizeof(Tp) ? SHRT_MAX \
: sizeof(int)==sizeof(Tp) ? INT_MAX \
: sizeof(long)==sizeof(Tp) ? LONG_MAX \
: sizeof(long long)==sizeof(Tp) ? LLONG_MAX : 0)) \
#define STC_ASSERT(X) ((void)(sizeof(struct { int stc_assert:(X)?1:-1; })))
int main()
{
STC_ASSERT(TP_MAX(signed char)==SCHAR_MAX);
STC_ASSERT(TP_MAX(short)==SHRT_MAX);
STC_ASSERT(TP_MAX(int)==INT_MAX);
STC_ASSERT(TP_MAX(long)==LONG_MAX);
STC_ASSERT(TP_MAX(long long)==LLONG_MAX);
STC_ASSERT(TP_MAX(unsigned char)==UCHAR_MAX);
STC_ASSERT(TP_MAX(unsigned short)==USHRT_MAX);
STC_ASSERT(TP_MAX(unsigned int)==UINT_MAX);
STC_ASSERT(TP_MAX(unsigned long)==ULONG_MAX);
STC_ASSERT(TP_MAX(unsigned long long)==ULLONG_MAX);
}
(If you want to do it even without limits.h, please check out my answer at https://stackoverflow.com/a/53470064/1084774).