-1

I want to define variables in structure based on endianness of machine , This is what I tried

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
//#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
struct A {
    #if IS_BIG_ENDIAN
    char a:4;
    char b:4;
    #else
    char b:4;
    char a:4;
    #endif
}

The above code is giving error , is this possible to achieve. My Main aim is to achieve reverse order of structure variables.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Arun Pal
  • 687
  • 7
  • 28
  • 3
    Possible duplicate of [C Macro definition to determine big endian or little endian machine?](https://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine) – Fire Lancer Apr 11 '19 at 14:11
  • Your macro is a runtime check when you want a preprocessor-time check! (not even a compile-time where you could use modern type traits that can give you endianness). – Matthieu Brucher Apr 11 '19 at 14:12
  • @FireLancer yes if wasn't about setting structure order, which it is, so no, this question isn't a duplicate. – Matthieu Brucher Apr 11 '19 at 14:12
  • FWIW, C++20 offers an enum value for this. https://en.cppreference.com/w/cpp/types/endian – NathanOliver Apr 11 '19 at 14:13
  • You could accomplish this without the preprocessor by using template specialization or `std::conditional_t`. Is there another reason you need this to be at PP time? – chris Apr 11 '19 at 14:16
  • 3
    Also, note that the layout of bit-fields is implementation-defined [\[class.bit\]/1](http://eel.is/c++draft/class.bit#1). So be aware that this is inherently not a portable way of doing things to begin with… – Michael Kenzel Apr 11 '19 at 14:18
  • 3
    [Byte order fallacy](https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html) tldr; the byte order of the machine **does not matter** what matters is the byte order of the data – Guillaume Racicot Apr 11 '19 at 14:19
  • The macro answers like `__BYTE_ORDER__ ` will work with the preprocessor. – Fire Lancer Apr 11 '19 at 14:22
  • 2
    Type punning via unions in general is UB and I'm not aware that there's an exception for `char` or `char[]` (as exists for pointers, all of can be legally casted to `char*`...) – Aconcagua Apr 11 '19 at 14:22
  • 1
    Why do you want that? Do you want serialise by just printing the bytes of your struct as is? That's dangerous in other respects, too – e. g. alignment: `struct S { int32_t i32; int64_t i64; };` might have a size of 12 on a 32 bit compiler, but of 16 on a 64 bit compiler due to byte padding in between the two members! – Aconcagua Apr 11 '19 at 14:32
  • 1
    Don't edit your **question** to contain a solution. The format is Q&A, so accept an answer if it holds a solution. – StoryTeller - Unslander Monica Apr 11 '19 at 14:55

4 Answers4

2

From https://sourceforge.net/p/predef/wiki/Endianness/

You don't have standard way to have it.

but custom headers <sys/param.h> or <endian.h> might provide MACRO such as:

Type                            Macro           Value
Big endian                      __BYTE_ORDER    __BIG_ENDIAN
Little endian                   __BYTE_ORDER    __LITTLE_ENDIAN
Little endian (word-swapped)    __BYTE_ORDER    __PDP_ENDIAN
Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

That test cannot work for a pre-processor if-directive.

The upcoming C++20 is proposed to have std::endian. It can be used without the pre processor.

static_assert(std::endian::native == std::endian::big
           || std::endian::native == std::endian::little);

struct ABig {
    char a:4;
    char b:4;
};

struct ALit {
    char b:4;
    char a:4;
};

using A = std::conditional_t<std::endian::native == std::endian::big, ABig, ALit>;

Prior to C++20, there is no standard way of getting the endianness.

A portable solution is to use pre-defined macros to detect the system. Some systems are little endian (Windows), while others provide a header with macro definitions (<endian.h> in Linux, <sys/endian.h> in BSD and so on) and others may be different in some other way. The GCC compiler provides macros:

__BYTE_ORDER__
__ORDER_LITTLE_ENDIAN__
__ORDER_BIG_ENDIAN__
__ORDER_PDP_ENDIAN__

That said, pretty much every aspect of the representation of bit fields is implementation defined, and if it is important for the order to be exact - as it appears to be - then you're going to have to rely on particular implementation anyway, so portability is not achievable. Decide your target system and once you've decided, consult the documentation of that system for how to get the endianness.

It's best to not rely on the representation of bit fields.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

Wait until C++20 (or use experimental features of your compiler) and you will be able to use C++ standard std::endian for this:

Indicates the endianness of all scalar types:

If all scalar types are little-endian, std::endian::native equals std::endian::little If all scalar types are big-endian, std::endian::native equals std::endian::big

SergeyA
  • 61,605
  • 5
  • 78
  • 137
-1

Thanks @Jarod42 , following code is working for me

#define IS_BIG_ENDIAN (__BYTE_ORDER__ == __BIG_ENDIAN)
struct A {
    #if IS_BIG_ENDIAN
    char a:4;
    char b:4;
    #else
    char b:4;
    char a:4;
    #endif
};
Czipperz
  • 3,268
  • 2
  • 18
  • 25
Arun Pal
  • 687
  • 7
  • 28