boostorg / predef

Boost.Predef (a Boost C++ Library)
47 stars 66 forks source link

unique endian macros #130

Open gpeterhoff opened 1 year ago

gpeterhoff commented 1 year ago

Hello, often the mistake is made (happened to me too) to consider only 2 cases when checking the endianess, eg:

struct foo {

if defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE)

uint32_t a : 16; uint32_t b : 16;

else

uint32_t b : 16; uint32_t a : 16;

endif

};

because the else branch is not necessarily BOOST_ENDIAN_BIG_BYTE_AVAILABLE. More correctly, this example must read:

struct foo {

if defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE)

uint32_t a : 16; uint32_t b : 16;

elif defined(BOOST_ENDIAN_BIG_BYTE_AVAILABLE)

uint32_t b : 16; uint32_t a : 16;

else

error or

specializations for BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE/BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE

endif

};

This is error-prone and usually overlooked. Therefore it would make sense to provide unique macros that guarantee that ONLY the contrary endianess is used in the else-branch, eg endian.hpp.txt. Now we can use correctly:

struct foo {

if defined(BOOST_ENDIAN_LITTLE_BYTE_UNIQUE_AVAILABLE)

uint32_t a : 16; uint32_t b : 16;

else

uint32_t b : 16; uint32_t a : 16;

endif

};

But this means that these unique-macros are actually used. It would probably make more sense to "teach" this behavior to the existing macros.

thx & regards Gero

andry81 commented 1 year ago

The last example looks like the first example. Why you think it would work differently?

https://www.boost.org/doc/libs/1_82_0/boost/predef/other/endian.h


#if BOOST_ENDIAN_BIG_BYTE
#   define BOOST_ENDIAN_BIG_BYTE_AVAILABLE
#endif
#if BOOST_ENDIAN_BIG_WORD
#   define BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE
#endif
#if BOOST_ENDIAN_LITTLE_BYTE
#   define BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE
#endif
#if BOOST_ENDIAN_LITTLE_WORD
#   define BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE
#endif

#define BOOST_ENDIAN_BIG_BYTE_NAME "Byte-Swapped Big-Endian"
#define BOOST_ENDIAN_BIG_WORD_NAME "Word-Swapped Big-Endian"
#define BOOST_ENDIAN_LITTLE_BYTE_NAME "Byte-Swapped Little-Endian"
#define BOOST_ENDIAN_LITTLE_WORD_NAME "Word-Swapped Little-Endian"
gpeterhoff commented 1 year ago

You have overlooked the UNIQUE in the macro. In the first example, as I said, several cases can occur in the else branch:

With the UNIQUE-macros these ambiguities are excluded.

thx & regards Gero

andry81 commented 1 year ago

You have overlooked the UNIQUE in the macro.

I am not.

With the UNIQUE-macros these ambiguities are excluded.

It still not clear how.

If you wrote something like:

#if defined(BOOST_ENDIAN_LITTLE_BYTE_LITTLE_WORD_LITTLE_DWORD_LITTLE_QWORD_BLABLABLA_AVAILABLE)

then, it would be more unique.

gpeterhoff commented 1 year ago

I thought my (brief) comments would be sufficient. But I would like to explain it to you in detail:

// WORD ?

if defined(BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE) || defined(BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE)

define BOOST_ENDIAN_WORD_AVAILABLE

endif

BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE or BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE is available - independent of BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE or BOOST_ENDIAN_BIG_BYTE_AVAILABLE

// BYTE ?

if defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE) || defined(BOOST_ENDIAN_BIG_BYTE_AVAILABLE))

define BOOST_ENDIAN_BYTE_AVAILABLE

endif

BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE or BOOST_ENDIAN_BIG_BYTE_AVAILABLE is available - independent of BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE or BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE

// unique WORD or BYTE ?

if (defined(BOOST_ENDIAN_WORD_AVAILABLE) && !defined(BOOST_ENDIAN_BYTE_AVAILABLE)) || / WORD only / \

(!defined(BOOST_ENDIAN_WORD_AVAILABLE) && defined(BOOST_ENDIAN_BYTE_AVAILABLE)) / BYTE only /

define BOOST_ENDIAN_UNIQUE_AVAILABLE

endif

BOOST_ENDIAN_UNIQUE_AVAILABLE is defined only if BOOST_ENDIAN_WORD_AVAILABLE or BOOST_ENDIAN_BYTE_AVAILABLE is defined - exclusive or. If both/no definitions exist, BOOST_ENDIAN_UNIQUE_AVAILABLE is not defined. This makes it possible to clearly distinguish whether the endianness is WORD- or BYTE-based.

// unique WORD

if defined(BOOST_ENDIAN_UNIQUE_AVAILABLE) && defined(BOOST_ENDIAN_WORD_AVAILABLE)

define BOOST_ENDIAN_WORD_UNIQUE_AVAILABLE

endif

helper, is only defined if the unique endianness is WORD-based

// unique BYTE

if defined(BOOST_ENDIAN_UNIQUE_AVAILABLE) && defined(BOOST_ENDIAN_BYTE_AVAILABLE)

define BOOST_ENDIAN_BYTE_UNIQUE_AVAILABLE

endif

helper, is only defined if the unique endianness is BYTE-based

// unique LITTLE_WORD

if defined(BOOST_ENDIAN_WORD_UNIQUE_AVAILABLE) && defined(BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE)

define BOOST_ENDIAN_LITTLE_WORD_UNIQUE_AVAILABLE

endif

is only defined if the unique endianness is WORD-based and LITTLE_WORD

// unique BIG_WORD

if defined(BOOST_ENDIAN_WORD_UNIQUE_AVAILABLE) && defined(BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE)

define BOOST_ENDIAN_BIG_WORD_UNIQUE_AVAILABLE

endif

is only defined if the unique endianness is WORD-based and BIG_WORD

This can rule out that there is a BYTE-based endianness. If BOOST_ENDIAN_LITTLE_WORD_UNIQUE_AVAILABLE is defined, the complement (#else) BOOST_ENDIAN_BIG_WORD_UNIQUE_AVAILABLE is mandatory. If BOOST_ENDIAN_BIG_WORD_UNIQUE_AVAILABLE is defined, the complement (#else) BOOST_ENDIAN_LITTLE_WORD_UNIQUE_AVAILABLE is mandatory.

// unique LITTLE_BYTE

if defined(BOOST_ENDIAN_BYTE_UNIQUE_AVAILABLE) && defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE)

define BOOST_ENDIAN_LITTLE_BYTE_UNIQUE_AVAILABLE

endif

is only defined if the unique endianness is BYTE-based and LITTLE_BYTE

// unique BIG_BYTE

if defined(BOOST_ENDIAN_BYTE_UNIQUE_AVAILABLE) && defined(BOOST_ENDIAN_BIG_BYTE_AVAILABLE)

define BOOST_ENDIAN_BIG_BYTE_UNIQUE_AVAILABLE

endif

is only defined if the unique endianness is BYTE-based and BIG_BYTE

This can be ruled out that a WORD-based endianness is present. If BOOST_ENDIAN_LITTLE_BYTE_UNIQUE_AVAILABLE is defined, the complement (#else) BOOST_ENDIAN_BIG_BYTE_UNIQUE_AVAILABLE is mandatory. If BOOST_ENDIAN_BIG_BYTE_UNIQUE_AVAILABLE is defined, the complement (#else) BOOST_ENDIAN_LITTLE_BYTE_UNIQUE_AVAILABLE is mandatory.

cu Gero

andry81 commented 1 year ago
#if defined(BOOST_ENDIAN_LITTLE_WORD_BYTE_AVAILABLE) || defined(BOOST_ENDIAN_BIG_WORD_BYTE_AVAILABLE)

Why you need to check both? Either you use little or big endian.

Your unique term does not mean unique actually, it means more like not nested endian. The unique here would be a specific combination of byte and word endians.

These lines of code is quite hard to read mess. Why not write directly?

#if BOOST_ENDIAN_BIG_BYTE || BOOST_ENDIAN_LITTLE_BYTE
#  define BOOST_ENDIAN_BYTE_UNIQUE_AVAILABLE
#  if !BOOST_ENDIAN_BIG_WORD
#    if !BOOST_ENDIAN_LITTLE_WORD
#      define BOOST_ENDIAN_UNIQUE_AVAILABLE
//...
#endif
#if BOOST_ENDIAN_BIG_WORD || BOOST_ENDIAN_LITTLE_WORD
#  define BOOST_ENDIAN_WORD_UNIQUE_AVAILABLE
#  if !BOOST_ENDIAN_BIG_BYTE
#    if !BOOST_ENDIAN_LITTLE_BYTE
#      define BOOST_ENDIAN_UNIQUE_AVAILABLE
//...
#endif

It would be much more readable.

gpeterhoff commented 1 year ago

Why you need to check both? Either you use little or big endian.

This is exactly the conclusion. It is NOT guaranteed that in the case

because other combinations can occur

https://en.cppreference.com/w/cpp/types/endian (mixed-end, PDP). With the UNIQUE macros it can be guaranteed that ONLY the complement in the else-branch is used.

thx Gero

andry81 commented 1 year ago

It is NOT guaranteed that in the case

It does not guarantee either in your case, because you always need to write the else branch no matter what macro you are using. In both cases you can forget that. So it still is not clear why this need.