C++20 より前でも std::endian を使いたいので書いてみる。 gcc/clang のプリプロセッサ定義 BYTE_ORDER, ORDER_BIG_ENDIAN, ORDER_LITTLE_ENDIAN
を定義されていないコンパイラでも強引に割り当てて使うことにします。
動作確認している環境は以下の通り
- Windows 11 (CPU: Core i7)
- Visual Studio 2022 Version 17.10.1
- Microsoft Visual C++ 2022
- Visual Studio 2022 Version 17.10.1
- macOS 14.5 (CPU: Core i7)
- Apple clang version 15.0.0 (clang-1500.3.9.4)
- x86_64-apple-darwin23.5.0
- Apple clang version 15.0.0 (clang-1500.3.9.4)
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;
}
実行結果: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
実行結果: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 より前(言語オプションは既定)
実行結果:MSVC
__cplusplus = 199711
_MSVC_LANG = 201402
std::endian
native = 0x4d2 (1234)
big = 0x10e1 (4321)
little = 0x4d2 (1234)
ok: native == little
実行結果:Clang
$ clang++ sample.cpp && ./a.out
__cplusplus = 199711
std::endian
native = 0x4d2 (1234)
big = 0x10e1 (4321)
little = 0x4d2 (1234)
ok: native == little