C++20 より前でも std::endian を。(big は face で little は dead ？)


https://en.cppreference.com/w/cpp/types/endian

https://cpprefjp.github.io/reference/bit/endian.html

C++20 より前でも std::endian を使いたいので書いてみる。 gcc/clang のプリプロセッサ定義 <code>__BYTE_ORDER__, __ORDER_BIG_ENDIAN__, __ORDER_LITTLE_ENDIAN__</code> を定義されていないコンパイラでも強引に割り当てて使うことにします。

動作確認している環境は以下の通り

- Windows 11 (CPU: Core i7)
  - Visual Studio 2022 Version 17.10.1
    - Microsoft Visual C++ 2022
- macOS 14.5 (CPU: Core i7)
  - Apple clang version 15.0.0 (clang-1500.3.9.4)
    - x86_64-apple-darwin23.5.0

```c++:sample.cpp
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
#include <bit>
#endif

#if __cpp_lib_endian < 201907L

#if !(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__))
#  if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(_M_ARM)
#    define __ORDER_BIG_ENDIAN__     4321
#    define __ORDER_LITTLE_ENDIAN__  1234
#    define __BYTE_ORDER__           __ORDER_LITTLE_ENDIAN__
#  else
#    error "unknown compiler."
#  endif
#endif

namespace std
{
#if __cplusplus < 201103L && _MSVC_LANG < 201103L

    struct endian
    {
        enum value_type
        {
            big = __ORDER_BIG_ENDIAN__,
            little = __ORDER_LITTLE_ENDIAN__,
            native = __BYTE_ORDER__,
        };
    };

#else  /* C++11 */

    enum class endian
    {
        big = __ORDER_BIG_ENDIAN__,
        little = __ORDER_LITTLE_ENDIAN__,
        native = __BYTE_ORDER__,
    };

#endif
} // std

#endif /* std::endian */

// ----------------------------------------------------------------------------

#include <iostream>

int main()
{
    union
    {
        unsigned char b[2];
        unsigned short w;
    } u;
    u.w = 0x1234;

#ifdef __cplusplus
    std::cout << "__cplusplus = " << __cplusplus << std::endl;
#endif
#ifdef _MSVC_LANG
    std::cout << "_MSVC_LANG = " << _MSVC_LANG << std::endl;
#endif
#ifdef __cpp_lib_endian
    std::cout << "__cpp_lib_endian = " << __cpp_lib_endian << std::endl;
#endif

    std::cout << "std::endian" << std::endl
              << "  native = 0x" << std::hex << int(std::endian::native)
              << " (" << std::dec << int(std::endian::native) << ")" << std::endl
              << "  big = 0x" << std::hex << int(std::endian::big)
              << " (" << std::dec << int(std::endian::big) << ")" << std::endl
              << "  little = 0x" << std::hex << int(std::endian::little)
              << " (" << std::dec << int(std::endian::little) << ")" << std::endl;

    if (u.b[0] == 0x12 && u.b[1] == 0x34)
    {
        if (std::endian::native == std::endian::big)
            std::cout << "ok: native == big" << std::endl;
        else
            std::cout << "error: native == big" << std::endl;
    }
    else if (u.b[0] == 0x34 && u.b[1] == 0x12)
    {
        if (std::endian::native == std::endian::little)
            std::cout << "ok: native == little" << std::endl;
        else
            std::cout << "error: native == little" << std::endl;
    }
    else
    {
        std::cout << "???" << std::endl;
    }

    return 0;
}
```

```console:実行結果:MSVC オプション /std:c++20
__cplusplus = 199711
_MSVC_LANG = 202002
__cpp_lib_endian = 201907
std::endian
  native = 0x0 (0)
  big = 0x1 (1)
  little = 0x0 (0)
ok: native == little
```

```console:実行結果:Clang オプション -std=c++20
$ clang++ -std=c++20 sample.cpp && ./a.out
__cplusplus = 202002
__cpp_lib_endian = 201907
std::endian
  native = 0xdead (57005)
  big = 0xface (64206)
  little = 0xdead (57005)
ok: native == little
```

Clang では big は 0xface で little は 0xdead と定義されているようです。

**C++20 より前(言語オプションは既定)**

```console:実行結果:MSVC
__cplusplus = 199711
_MSVC_LANG = 201402
std::endian
  native = 0x4d2 (1234)
  big = 0x10e1 (4321)
  little = 0x4d2 (1234)
ok: native == little
```

```console:実行結果:Clang
$ clang++ sample.cpp && ./a.out
__cplusplus = 199711
std::endian
  native = 0x4d2 (1234)
  big = 0x10e1 (4321)
  little = 0x4d2 (1234)
ok: native == little
```