How to Do a C++ Style Compile-Time Assertion to Determine MAChine's Endianness

Is there a way to do a C++ style compile-time assertion to determine machine's endianness?

If you're using autoconf, you can use the AC_C_BIGENDIAN macro, which is fairly guaranteed to work (setting the WORDS_BIGENDIAN define by default)

alternately, you could try something like the following (taken from autoconf) to get a test that will probably be optimized away (GCC, at least, removes the other branch)

int is_big_endian()
{
union {
long int l;
char c[sizeof (long int)];
} u;

u.l = 1;

if (u.c[sizeof(long int)-1] == 1)
{
return 1;
}
else
return 0;
}

Determining endianness at compile time

This is for compile time checking

You could use information from the boost header file endian.hpp, which covers many platforms.

edit for runtime checking

bool isLittleEndian()
{
short int number = 0x1;
char *numPtr = (char*)&number;
return (numPtr[0] == 1);
}

Create an integer, and read its first byte (least significant byte). If that byte is 1, then the system is little endian, otherwise it's big endian.

edit Thinking about it

Yes you could run into a potential issue in some platforms (can't think of any) where sizeof(char) == sizeof(short int). You could use fixed width multi-byte integral types available in <stdint.h>, or if your platform doesn't have it, again you could adapt a boost header for your use: stdint.hpp

In C++11 or later is there a way to have a constexpr that determines endian without UB?

As part of my hash_append work I hope to provide what you're asking for:

https://github.com/HowardHinnant/hash_append/blob/master/endian.h

other would be detected by:

endian::native != endian::little && endian::native != endian::big

The first static_assert in this header is currently incorrect with respect to the other issue and should be removed.

This header is very easy to provide for any given platform. But of course it is not portable, and thus is ideal to have it be provided by your std::lib implementor instead.

Detecting Endianness

At compile time in C you can't do much more than trusting preprocessor #defines, and there are no standard solutions because the C standard isn't concerned with endianness.

Still, you could add an assertion that is done at runtime at the start of the program to make sure that the assumption done when compiling was true:

inline int IsBigEndian()
{
int i=1;
return ! *((char *)&i);
}

/* ... */

#ifdef COMPILED_FOR_BIG_ENDIAN
assert(IsBigEndian());
#elif COMPILED_FOR_LITTLE_ENDIAN
assert(!IsBigEndian());
#else
#error "No endianness macro defined"
#endif

(where COMPILED_FOR_BIG_ENDIAN and COMPILED_FOR_LITTLE_ENDIAN are macros #defined previously according to your preprocessor endianness checks)

How do I convert between big-endian and little-endian values in C++?

If you're using Visual C++ do the following: You include intrin.h and call the following functions:

For 16 bit numbers:

unsigned short _byteswap_ushort(unsigned short value);

For 32 bit numbers:

unsigned long _byteswap_ulong(unsigned long value);

For 64 bit numbers:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 bit numbers (chars) don't need to be converted.

Also these are only defined for unsigned values they work for signed integers as well.

For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order. You can get little-endian floats on big-endian machines and vice versa.

Other compilers have similar intrinsics as well.

In GCC for example you can directly call some builtins as documented here:

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(no need to include something). Afaik bits.h declares the same function in a non gcc-centric way as well.

16 bit swap it's just a bit-rotate.

Calling the intrinsics instead of rolling your own gives you the best performance and code density btw..

Verify macro argument size at compilation time

The type of the ternary ?: expression is the common type of its second and third arguments (with integer promotion of smaller types). So the following version of your MY_MACRO will work in a 32-bit architecture:

static_assert(sizeof(uint32_t) == sizeof 0, ""); // sanity check, for your machine

#define MY_MACRO(arg0, arg1, arg2) \
do { \
static_assert(sizeof(0 ? 0 : (arg0)) == sizeof 0, ""); \
static_assert(sizeof(0 ? 0 : (arg1)) == sizeof 0, ""); \
static_assert(sizeof(0 ? 0 : (arg2)) == sizeof 0, ""); \
my_macro_impl((uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2)); \
} while (0)

Moreover, this solution should work with all versions of C and C++ (with, if necessary, a suitable definition of static_assert).

Note this macro, like the OP's original, has function semantics in that the arguments are evaluated only once, unlike for example the notorious MAX macro.



Related Topics



Leave a reply



Submit