std::endian と std::byteswap からエンディアン固定型を作る


C++03/11/14/17/20 各々の言語仕様を使いつつ実装すると勉強になります。

実装対象をビッグ エンディアンとリトル エンディアンに限定します。

動作確認は

- macOS 14.5
  - Apple clang version 15.0.0 (clang-1500.3.9.4)
    - Target: x86_64-apple-darwin23.5.0

なので、ビッグ エンディアン機でのテストはしていません。


# C++20 より前でも std::endian と std::byteswap を用意

https://qiita.com/ikiuo/items/562de529442826aecbc0

<details><summary>ヘッダ ファイル : endian.hpp</summary><div>

```C++:endian.hpp
#pragma once

#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 /* C++11 */
} // std

#endif /* __cpp_lib_endian < 201907L */
```

</div></details>

https://qiita.com/ikiuo/items/519811088e75aac7d9c9

<details><summary>ヘッダ ファイル : byteswap.hpp</summary><div>

```C++:byteswap.hpp
#pragma once

#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
#include <bit>
#endif

#if __cpp_lib_byteswap < 202110L

namespace std
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4293 4333 26452)
#endif /* MSVC */

    template <typename T>

#if __cplusplus < 201103L && _MSVC_LANG < 201103L
    inline
#  if defined(__GNUC__) || defined(__clang__)
    __attribute__((const, pure))
#  endif
    T byteswap(T x)
#else  /* C++11 */

    inline constexpr T byteswap(T x) noexcept

#endif
    {
        return (sizeof(T) == 8
                ? T((((x >> 000) & 0xff) << 070) |
                    (((x >> 010) & 0xff) << 060) |
                    (((x >> 020) & 0xff) << 050) |
                    (((x >> 030) & 0xff) << 040) |
                    (((x >> 040) & 0xff) << 030) |
                    (((x >> 050) & 0xff) << 020) |
                    (((x >> 060) & 0xff) << 010) |
                    (((x >> 070) & 0xff) << 000))
                : sizeof(T) == 4
                ? T((((x >> 000) & 0xff) << 030) |
                    (((x >> 010) & 0xff) << 020) |
                    (((x >> 020) & 0xff) << 010) |
                    (((x >> 030) & 0xff) << 000))
                : sizeof(T) == 2
                ? T((((x >> 000) & 0xff) << 010) |
                    (((x >> 010) & 0xff) << 000))
                : x);
    }

#ifdef _MSC_VER
#pragma warning(pop)
#endif /* MSVC */
} // std

#endif /* __cpp_lib_byteswap < 202110L */
```

</div></details>


# 実装方法

- [C++03 でも std::conditional が使用可能な処理系](https://cpprefjp.github.io/reference/type_traits/conditional.html)を前提としています。

まず、(整数/浮動小数点/文字)型 <code>T</code> をテンプレート引数とするクラス byteswap_value 作ります。

```C++:byteswap値型テンプレート&nbsp;クラス
template <typename T> class byteswap_value;
```

CPU が直接扱う型を <code>native_endian::型</code> で参照できるようにします。

```C++:native値型表クラス
struct native_endian {
    enum {
        is_big = std::endian::big == std::endian::native,
        is_little = std::endian::little == std::endian::native,
    };
    typedef std::int8_t int8_type;
    typedef std::uint8_t uint8_type;
    typedef std::int16_t int16_type;
    typedef std::uint16_t uint16_type;
    // ...(略)...
};
```

これに対応する byteswap 型を <code>byteswap_endian::型</code> で参照できるようにします。

```C++:byteswap値型表クラス
struct byteswap_endian {
    enum {
        is_big = !native_endian::is_big,
        is_little = !native_endian::is_little,
    };
    typedef native_endian::int8_type int8_type;
    typedef native_endian::uint8_type uint8_type;
    typedef byteswap_value<native_endian::int16_type> int16_type;
    typedef byteswap_value<native_endian::uint16_type> uint16_type;
    // ...(略)...
};
```

<code>native_endian</code> か <code>byteswap_endian</code> を選択するためのテンプレート <code>fixed_endian</code> を用意します。

```C++:エンディアン選択テンプレート
template <bool big_endian>
struct fixed_endian : std::conditional<
    big_endian == native_endian::is_big,
    native_endian, byteswap_endian>::type {};
```

ビッグ エンディアンは<code>_t</code>の前に<code>be</code>を付けて<code>be_t</code>に、同様にリトル エンディアンは<code>le_t</code>とする型を定義します。

```C++:ビッグ&nbsp;エンディアン型
typedef fixed_endian<true> big_endian;
typedef big_endian::int8_type int8be_t;
typedef big_endian::uint8_type uint8be_t;
typedef big_endian::int16_type int16be_t;
typedef big_endian::uint16_type uint16be_t;
// ...(略)...
```

```C++:リトル&nbsp;エンディアン型
typedef fixed_endian<false> little_endian;
typedef little_endian::int8_type int8le_t;
typedef little_endian::uint8_type uint8le_t;
typedef little_endian::int16_type int16le_t;
typedef little_endian::uint16_type uint16le_t;
// ...(略)...
```

## C++03 で書いてみる

初期化にはコンストラクタが必要

```C++:例
byteswap_value<int> sw(1);
byteswap_value<int> sw = 1;
```

だから、コンストラクタを定義すると trivial 型ではなくなる。すると、
　　<code>std::basic_string&lt; byteswap_value&lt;wchar_t&gt; &gt;</code>
は <code>clang++</code> ではコンパイル エラーが出る。

二項(T型とU型の)演算の返り値の型推定が少し面倒だった。

- (int, T, U) の中で一番大きい正の数を表現できる型


```C++:byteswap_value型
template <typename T>
class byteswap_value
{
public:
    typedef T value_type;  // byteswap する native の型.

protected:

    /* T が整数型時の情報 */
    template <typename I>
    struct integer_type
    {
        enum {
            is_signed = std::numeric_limits<I>::is_signed,
            bits = sizeof(I) * 8 - (is_signed ? 1 : 0),
        };
        typedef I type;
    };

    /* T が浮動小数点型時の情報 */
    template <typename I, int B>
    struct floating_type
    {
        enum { bits = B };
        typedef I type;
    };

    /* 逆バイト順型 */
    template <typename I> struct bswap : integer_type<I> {};
    template <> struct bswap<float> : floating_type<std::uint32_t, 128> {};
    template <> struct bswap<double> : floating_type<std::uint64_t, 1024> {};

    typedef typename bswap<T>::type bswap_value;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline bswap_cast(const T& n) : v(n) {}
        inline operator bswap_value() const
            { return std::byteswap(*(const bswap_value*)&v); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline native_cast(const bswap_value& s) : v(std::byteswap(s)) {}
        inline operator T() const { return *(const T*)&v; }
    };

protected:

    /* 二項演算結果の型推定 */
    template <typename U>
    struct retval
    {
        template <typename V> struct chrint { typedef V type; };
        template <> struct chrint<wchar_t> { typedef wint_t type; };

        enum {
            t_bits = bswap<T>::bits,
            u_bits = bswap<U>::bits,
            t_flag = t_bits > u_bits,
            r_bits = t_flag ? t_bits : u_bits,

            i_bits = bswap<int>::bits,
            r_flag = r_bits > i_bits,
        };

        typedef typename std::conditional<t_flag, T, U>::type R;
        typedef typename std::conditional<r_flag, R, int>::type V;
        typedef typename chrint<V>::type type;
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    inline byteswap_value() {}
    inline byteswap_value(T d) : _v(bswap_cast(d)) {}
    inline byteswap_value(const byteswap_value<T>& s) : _v(s._v) {}

    inline operator T() const { return get(); }

    inline bool operator !() const { return !_v; }
    inline bool operator ==(const byteswap_value<T>& s) const { return _v == s._v; }
    inline bool operator !=(const byteswap_value<T>& s) const { return _v != s._v; }

    template <typename U> inline bool operator < (U n) const { return get() <  n; }
    template <typename U> inline bool operator <=(U n) const { return get() <= n; }
    template <typename U> inline bool operator ==(U n) const { return get() == n; }
    template <typename U> inline bool operator !=(U n) const { return get() != n; }
    template <typename U> inline bool operator >=(U n) const { return get() >= n; }
    template <typename U> inline bool operator > (U n) const { return get() >  n; }

    inline T operator +() const { return +get(); }
    inline T operator -() const { return -get(); }
    inline T operator ~() const { return ~get(); }

    template <typename U> inline typename retval<U>::type operator +(U n) const { return get() + n; }
    template <typename U> inline typename retval<U>::type operator -(U n) const { return get() - n; }
    template <typename U> inline typename retval<U>::type operator *(U n) const { return get() * n; }
    template <typename U> inline typename retval<U>::type operator /(U n) const { return get() / n; }
    template <typename U> inline typename retval<U>::type operator %(U n) const { return get() % n; }

    template <typename U> inline typename retval<U>::type operator &(U n) const { return get() & n; }
    template <typename U> inline typename retval<U>::type operator |(U n) const { return get() | n; }
    template <typename U> inline typename retval<U>::type operator ^(U n) const { return get() ^ n; }

    inline T operator <<(int n) const { return get() << n; }
    inline T operator >>(int n) const { return get() >> n; }

    inline byteswap_value<T>& operator =(T n) { return set(n); }
    inline byteswap_value<T>& operator =(const byteswap_value<T>& s) { _v = s._v; return *this; }

    template <typename U> inline byteswap_value<T>& operator +=(U n) { return set(get() + n); }
    template <typename U> inline byteswap_value<T>& operator -=(U n) { return set(get() - n); }
    template <typename U> inline byteswap_value<T>& operator *=(U n) { return set(get() * n); }
    template <typename U> inline byteswap_value<T>& operator /=(U n) { return set(get() / n); }
    template <typename U> inline byteswap_value<T>& operator %=(U n) { return set(get() % n); }

    inline byteswap_value<T>& operator &=(T n) { return set(get() & n); }
    inline byteswap_value<T>& operator |=(T n) { return set(get() | n); }
    inline byteswap_value<T>& operator ^=(T n) { return set(get() ^ n); }

    inline byteswap_value<T>& operator <<=(int n) { return set(get() << n); }
    inline byteswap_value<T>& operator >>=(int n) { return set(get() >> n); }

protected:
    inline T get() const { return native_cast(_v); }
    inline byteswap_value<T>& set(T n) { _v = bswap_cast(n); return *this; }
};
```


### テスト プログラム

<details><summary>endian-03.cpp (やや長いので折りたたみ)</summary><div>

```C++:endian-03.cpp
#include "endian.hpp"
#include "byteswap.hpp"

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

#include <cstdint>
#include <limits>
#include <string>

/*
 * byteswap_value
 */
template <typename T>
class byteswap_value
{
public:
    typedef T value_type;  // byteswap する native の型.

protected:

    /* T が整数型時の情報 */
    template <typename I>
    struct integer_type
    {
        enum {
            is_signed = std::numeric_limits<I>::is_signed,
            bits = sizeof(I) * 8 - (is_signed ? 1 : 0),
        };
        typedef I type;
    };

    /* T が浮動小数点型時の情報 */
    template <typename I, int B>
    struct floating_type
    {
        enum { bits = B };
        typedef I type;
    };

    /* 逆バイト順型 */
    template <typename I> struct bswap : integer_type<I> {};
    template <> struct bswap<float> : floating_type<std::uint32_t, 128> {};
    template <> struct bswap<double> : floating_type<std::uint64_t, 1024> {};

    typedef typename bswap<T>::type bswap_value;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline bswap_cast(const T& n) : v(n) {}
        inline operator bswap_value() const
            { return std::byteswap(*(const bswap_value*)&v); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline native_cast(const bswap_value& s) : v(std::byteswap(s)) {}
        inline operator T() const { return *(const T*)&v; }
    };

protected:

    /* 二項演算結果の型推定 */
    template <typename U>
    struct retval
    {
        template <typename V> struct chrint { typedef V type; };
        template <> struct chrint<wchar_t> { typedef wint_t type; };

        enum {
            t_bits = bswap<T>::bits,
            u_bits = bswap<U>::bits,
            t_flag = t_bits > u_bits,
            r_bits = t_flag ? t_bits : u_bits,

            i_bits = bswap<int>::bits,
            r_flag = r_bits > i_bits,
        };

        typedef typename std::conditional<t_flag, T, U>::type R;
        typedef typename std::conditional<r_flag, R, int>::type V;
        typedef typename chrint<V>::type type;
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    inline byteswap_value() {}
    inline byteswap_value(T d) : _v(bswap_cast(d)) {}
    inline byteswap_value(const byteswap_value<T>& s) : _v(s._v) {}

    inline operator T() const { return get(); }

    inline bool operator !() const { return !_v; }
    inline bool operator ==(const byteswap_value<T>& s) const { return _v == s._v; }
    inline bool operator !=(const byteswap_value<T>& s) const { return _v != s._v; }

    template <typename U> inline bool operator < (U n) const { return get() <  n; }
    template <typename U> inline bool operator <=(U n) const { return get() <= n; }
    template <typename U> inline bool operator ==(U n) const { return get() == n; }
    template <typename U> inline bool operator !=(U n) const { return get() != n; }
    template <typename U> inline bool operator >=(U n) const { return get() >= n; }
    template <typename U> inline bool operator > (U n) const { return get() >  n; }

    inline T operator +() const { return +get(); }
    inline T operator -() const { return -get(); }
    inline T operator ~() const { return ~get(); }

    template <typename U> inline typename retval<U>::type operator +(U n) const { return get() + n; }
    template <typename U> inline typename retval<U>::type operator -(U n) const { return get() - n; }
    template <typename U> inline typename retval<U>::type operator *(U n) const { return get() * n; }
    template <typename U> inline typename retval<U>::type operator /(U n) const { return get() / n; }
    template <typename U> inline typename retval<U>::type operator %(U n) const { return get() % n; }

    template <typename U> inline typename retval<U>::type operator &(U n) const { return get() & n; }
    template <typename U> inline typename retval<U>::type operator |(U n) const { return get() | n; }
    template <typename U> inline typename retval<U>::type operator ^(U n) const { return get() ^ n; }

    inline T operator <<(int n) const { return get() << n; }
    inline T operator >>(int n) const { return get() >> n; }

    inline byteswap_value<T>& operator =(T n) { return set(n); }
    inline byteswap_value<T>& operator =(const byteswap_value<T>& s) { _v = s._v; return *this; }

    template <typename U> inline byteswap_value<T>& operator +=(U n) { return set(get() + n); }
    template <typename U> inline byteswap_value<T>& operator -=(U n) { return set(get() - n); }
    template <typename U> inline byteswap_value<T>& operator *=(U n) { return set(get() * n); }
    template <typename U> inline byteswap_value<T>& operator /=(U n) { return set(get() / n); }
    template <typename U> inline byteswap_value<T>& operator %=(U n) { return set(get() % n); }

    inline byteswap_value<T>& operator &=(T n) { return set(get() & n); }
    inline byteswap_value<T>& operator |=(T n) { return set(get() | n); }
    inline byteswap_value<T>& operator ^=(T n) { return set(get() ^ n); }

    inline byteswap_value<T>& operator <<=(int n) { return set(get() << n); }
    inline byteswap_value<T>& operator >>=(int n) { return set(get() >> n); }

protected:
    inline T get() const { return native_cast(_v); }
    inline byteswap_value<T>& set(T n) { _v = bswap_cast(n); return *this; }
};

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

#include <cstdint>

struct native_endian
{
    enum {
        is_big = std::endian::big == std::endian::native,
        is_little = std::endian::little == std::endian::native,
    };

    typedef std::int8_t int8_type;
    typedef std::int16_t int16_type;
    typedef std::int32_t int32_type;
    typedef std::int64_t int64_type;

    typedef std::uint8_t uint8_type;
    typedef std::uint16_t uint16_type;
    typedef std::uint32_t uint32_type;
    typedef std::uint64_t uint64_type;

    typedef float float32_type;
    typedef double float64_type;

    typedef char char_type;
    typedef wchar_t wchar_type;
};

struct byteswap_endian
{
    enum {
        is_big = !native_endian::is_big,
        is_little = !native_endian::is_little,
    };

    typedef native_endian::int8_type int8_type;
    typedef byteswap_value<native_endian::int16_type> int16_type;
    typedef byteswap_value<native_endian::int32_type> int32_type;
    typedef byteswap_value<native_endian::int64_type> int64_type;

    typedef native_endian::uint8_type uint8_type;
    typedef byteswap_value<native_endian::uint16_type> uint16_type;
    typedef byteswap_value<native_endian::uint32_type> uint32_type;
    typedef byteswap_value<native_endian::uint64_type> uint64_type;

    typedef byteswap_value<native_endian::float32_type> float32_type;
    typedef byteswap_value<native_endian::float64_type> float64_type;

    typedef byteswap_value<native_endian::char_type> char_type;
    typedef byteswap_value<native_endian::wchar_type> wchar_type;
};

template <bool big_endian>
struct fixed_endian : std::conditional<
    big_endian == native_endian::is_big,
    native_endian, byteswap_endian>::type {};

/* big endian */

typedef fixed_endian<true> big_endian;

typedef big_endian::int8_type int8be_t;
typedef big_endian::int16_type int16be_t;
typedef big_endian::int32_type int32be_t;
typedef big_endian::int64_type int64be_t;

typedef big_endian::uint8_type uint8be_t;
typedef big_endian::uint16_type uint16be_t;
typedef big_endian::uint32_type uint32be_t;
typedef big_endian::uint64_type uint64be_t;

typedef big_endian::float32_type float32be_t;
typedef big_endian::float64_type float64be_t;

typedef big_endian::char_type charbe_t;
typedef big_endian::wchar_type wcharbe_t;

/* little endian */

typedef fixed_endian<false> little_endian;

typedef little_endian::int8_type int8le_t;
typedef little_endian::int16_type int16le_t;
typedef little_endian::int32_type int32le_t;
typedef little_endian::int64_type int64le_t;

typedef little_endian::uint8_type uint8le_t;
typedef little_endian::uint16_type uint16le_t;
typedef little_endian::uint32_type uint32le_t;
typedef little_endian::uint64_type uint64le_t;

typedef little_endian::float32_type float32le_t;
typedef little_endian::float64_type float64le_t;

typedef little_endian::char_type charle_t;
typedef little_endian::wchar_type wcharle_t;

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

/*
 * テスト
 */

#include <algorithm>
#include <cstdio>
#include <typeinfo>

#ifndef _countof
#define _countof(a)  (sizeof(a) / sizeof(a[0]))
#endif

static const uint16_t native_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static uint16be_t big_data_u16[_countof(native_data_u16)];
static uint16le_t little_data_u16[_countof(native_data_u16)];

static const uint32_t native_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static uint32be_t big_data_u32[_countof(native_data_u32)];
static uint32le_t little_data_u32[_countof(native_data_u32)];

static const uint64_t native_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static uint64be_t big_data_u64[_countof(native_data_u64)];
static uint64le_t little_data_u64[_countof(native_data_u64)];

static const float native_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32be_t big_data_f32[_countof(native_data_f32)];
static float32le_t little_data_f32[_countof(native_data_f32)];

static const double native_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64be_t big_data_f64[_countof(native_data_f64)];
static float64le_t little_data_f64[_countof(native_data_f64)];

static void dumpbin(const char* msg, const void* s, size_t n)
{
    const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
    size_t x, y, ny = n & ~15, nr = n & 15;

    printf("address = %p: %s\n", s, msg);
    for (y = 0; y < ny; y += 16)
    {
        printf("   ");
        for (size_t x = 0; x < 16; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
    if (y < n)
    {
        printf("   ");
        for (x = 0; x < nr; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
}

template <typename T>
inline static void dumpbin(const char* msg, const T& s)
{
    dumpbin(msg, &s, sizeof(T));
}

template <typename T>
inline static const char *gettypename()
{
    return typeid(T()).name();
}

template <typename T>
inline static const char *gettypename(const T v)
{
    return typeid(v).name();
}

template <typename T>
inline static const char *valuetypename()
{
    return typeid(T(0)).name();
}

template <typename T>
static void rtypename(const char* name, const T& x)
{
    int8_t i8 = 11;
    int16_t i16 = 12;
    int32_t i32 = 14;
    int64_t i64 = 18;
    uint8_t u8 = 21;
    uint16_t u16 = 22;
    uint32_t u32 = 24;
    uint64_t u64 = 28;
    float f32 = 1.5;
    double f64 = 2.25;

    char c8 = 1;
    wchar_t wc = 2;

    printf("%s = %s\n", name, gettypename(x));
    printf("    +i8:%s  +i16:%s  +i32:%s  +i64:%s\n",
           gettypename(x + i8), gettypename(x + i16),
           gettypename(x + i32), gettypename(x + i64));
    printf("    +u8:%s  +u16:%s  +u32:%s  +u64:%s\n",
           gettypename(x + u8), gettypename(x + u16),
           gettypename(x + u32), gettypename(x + u64));
    printf("    +f32:%s  +f64:%s\n",
           gettypename(x + f32), gettypename(x + f64));
    printf("    +char:%s  +wchar:%s\n",
           gettypename(x + c8), gettypename(x + wc));
    printf("\n");

#if 0
    T y = 0;

    printf("    +=i8:%s  +=i16:%s  +=i32:%s  +i64:%s\n",
           gettypename(y += i8), gettypename(y += i16),
           gettypename(y += i32), gettypename(y += i64));
    printf("    +=u8:%s  +=u16:%s  +=u32:%s  +=u64:%s\n",
           gettypename(y += u8), gettypename(y += u16),
           gettypename(y += u32), gettypename(y += u64));
    printf("    +=f32:%s  +=f64:%s\n",
           gettypename(y += f32), gettypename(y += f64));
    printf("    +=c8:%s  +=wc:%s\n",
           gettypename(y += c8), gettypename(y += wc));
    printf("\n");
#endif
}

/*
 *
 */

int main()
{
    // big_data_XXX / little_data_XXX の初期化.
    std::copy(native_data_u16, native_data_u16 + _countof(native_data_u16), big_data_u16);
    std::copy(native_data_u16, native_data_u16 + _countof(native_data_u16), little_data_u16);
    std::copy(native_data_u32, native_data_u32 + _countof(native_data_u32), big_data_u32);
    std::copy(native_data_u32, native_data_u32 + _countof(native_data_u32), little_data_u32);
    std::copy(native_data_u64, native_data_u64 + _countof(native_data_u64), big_data_u64);
    std::copy(native_data_u64, native_data_u64 + _countof(native_data_u64), little_data_u64);
    std::copy(native_data_f32, native_data_f32 + _countof(native_data_f32), big_data_f32);
    std::copy(native_data_f32, native_data_f32 + _countof(native_data_f32), little_data_f32);
    std::copy(native_data_f64, native_data_f64 + _countof(native_data_f64), big_data_f64);
    std::copy(native_data_f64, native_data_f64 + _countof(native_data_f64), little_data_f64);

#ifdef __cplusplus
    printf("compiler: __cplusplus = %ld\n", __cplusplus);
#endif
#ifdef _MSVC_LANG
    printf("compiler: _MSVC_LANG = %ld\n", _MSVC_LANG);
#endif

    printf("endian: native = %s\n", native_endian::is_big ? "big" : "little");
    printf("\n");

    dumpbin("native_data_u16", native_data_u16);
    dumpbin("big_data_u16", big_data_u16);
    dumpbin("little_data_u16", little_data_u16);
    printf("\n");

    dumpbin("native_data_u32", native_data_u32);
    dumpbin("big_data_u32", big_data_u32);
    dumpbin("little_data_u32", little_data_u32);
    printf("\n");

    dumpbin("native_data_u64", native_data_u64);
    dumpbin("big_data_u64", big_data_u64);
    dumpbin("little_data_u64", little_data_u64);
    printf("\n");

    dumpbin("native_data_f32", native_data_f32);
    dumpbin("big_data_f32", big_data_f32);
    dumpbin("little_data_f32", little_data_f32);
    printf("\n");

    dumpbin("native_data_f64", native_data_f64);
    dumpbin("big_data_f64", big_data_f64);
    dumpbin("little_data_f64", little_data_f64);
    printf("\n");

    int16be_t i16be = 32;
    int32be_t i32be = 34;
    int64be_t i64be = 38;
    uint16be_t u16be = 42;
    uint32be_t u32be = 44;
    uint64be_t u64be = 48;
    float32be_t f32be = 4.25;
    float64be_t f64be = 8.125;

    int16le_t i16le = i16be;
    int32le_t i32le = i32be;
    int64le_t i64le = i64be;
    uint16le_t u16le = u16be;
    uint32le_t u32le = u32be;
    uint64le_t u64le = u64be;
    float32le_t f32le = f32be;
    float64le_t f64le = f64be;

    /**/

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    dumpbin("float32be_t", f32be); dumpbin("float32le_t", f32le);
    dumpbin("float64be_t", f64be); dumpbin("float64le_t", f64le);
    printf("\n");

    /**/

    u16be = 32; u32be = 34; u64be = 38;
    i16be = 42; i32be = 44; i64be = 48;
    u16le = 32; u32le = 34; u64le = 38;
    i16le = 42; i32le = 44; i64le = 48;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    u16be <<= 4; u32be <<= 4; u64be <<= 4;
    i16be <<= 4; i32be <<= 4; i64be <<= 4;
    u16le <<= 4; u32le <<= 4; u64le <<= 4;
    i16le <<= 4; i32le <<= 4; i64le <<= 4;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    printf("compare:\n");
#define COMP(l,op,r)                            \
    printf("    %s(%d) %-2s %s(%d) : %s\n",     \
           #l, (int)l, #op, #r, (int)r,         \
           (l op r) ? "true" : "false")
    COMP(i16be, < , i16le); COMP(i16be, <=, i16le); COMP(i16be, ==, i16le);
    COMP(i16be, !=, i16le); COMP(i16be, >=, i16le); COMP(i16be, > , i16le);
    printf("\n");
    COMP(i32be, < , i16le); COMP(i32be, <=, i16le); COMP(i32be, ==, i16le);
    COMP(i32be, !=, i16le); COMP(i32be, >=, i16le); COMP(i32be, > , i16le);
    printf("\n");
    COMP(i32be, < , i64be); COMP(i32be, <=, i64be); COMP(i32be, ==, i64be);
    COMP(i32be, !=, i64be); COMP(i32be, >=, i64be); COMP(i32be, > , i64be);
    printf("\n");
    COMP(i32be, < , i64le); COMP(i32be, <=, i64le); COMP(i32be, ==, i64le);
    COMP(i32be, !=, i64le); COMP(i32be, >=, i64le); COMP(i32be, > , i64le);
    printf("\n");

    /**/

    printf("typeid:\n");
    printf("    int8_t:%s  int16_t:%s  int32_t:%s  int64_t:%s\n",
           valuetypename<int8_t>(), valuetypename<int16_t>(),
           valuetypename<int32_t>(), valuetypename<int64_t>());
    printf("    uint8_t:%s  uint16_t:%s  uint32_t:%s  uint64_t:%s\n",
           valuetypename<uint8_t>(), valuetypename<uint16_t>(),
           valuetypename<uint32_t>(), valuetypename<uint64_t>());
    printf("    float:%s  double:%s\n", valuetypename<float>(), valuetypename<double>());
    printf("    char:%s  wchar_t:%s\n", valuetypename<char>(), valuetypename<wchar_t>());
    printf("    int:%s  unsigned:%s\n", valuetypename<int>(), valuetypename<unsigned>());
    printf("    sizeof(wchar_t):%zd\n", sizeof(wchar_t));
    printf("\n");

    /**/

    rtypename("int16be_t", i16be); rtypename("int16le_t", i16le);
    rtypename("int32be_t", i32be); rtypename("int32le_t", i32le);
    rtypename("int64be_t", i64be); rtypename("int64le_t", i64le);

    rtypename("uint16be_t", u16be); rtypename("uint16le_t", u16le);
    rtypename("uint32be_t", u32be); rtypename("uint32le_t", u32le);
    rtypename("uint64be_t", u64be); rtypename("uint64le_t", u64le);

    rtypename("float32be_t", f32be); rtypename("float32le_t", f32le);
    rtypename("float64be_t", f64be); rtypename("float64le_t", f64le);

    return 0;
}
```

```console:実行結果
$ clang++ -O3 -g0 -DNDEBUG=1 -std=c++03 endian-03.cpp -o endian-03
$ ./endian-03
compiler: __cplusplus = 199711
endian: native = little

address = 0x10c0eeaa0: native_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32
address = 0x10c0f0000: big_data_u16
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10c0f0060: little_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32

address = 0x10c0eeab0: native_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76
address = 0x10c0f0010: big_data_u32
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10c0f0070: little_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76

address = 0x10c0eeac0: native_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe
address = 0x10c0f0020: big_data_u64
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10c0f0080: little_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe

address = 0x10c0eead0: native_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76
address = 0x10c0f0030: big_data_f32
    34 56 78 9a 45 67 89 ab 56 78 9a bc 76 54 32 10
address = 0x10c0f0090: little_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76

address = 0x10c0eeae0: native_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76
address = 0x10c0f0040: big_data_f64
    34 56 78 9a bc de f0 12 45 67 89 ab cd ef 01 23
    56 78 9a bc de f0 12 34 76 54 32 10 fe dc ba 98
address = 0x10c0f00a0: little_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76

address = 0x7ff7b3e14630: i16be_t
    00 20
address = 0x7ff7b3e14636: i16le_t
    20 00
address = 0x7ff7b3e14620: u16be_t
    00 2a
address = 0x7ff7b3e1461e: u16le_t
    2a 00
address = 0x7ff7b3e14628: i32be_t
    00 00 00 22
address = 0x7ff7b3e14600: i32le_t
    22 00 00 00
address = 0x7ff7b3e14618: u32be_t
    00 00 00 2c
address = 0x7ff7b3e14604: u32le_t
    2c 00 00 00
address = 0x7ff7b3e14610: i64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7b3e14608: i64le_t
    26 00 00 00 00 00 00 00
address = 0x7ff7b3e145f8: u64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7b3e145f0: u64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7b3e145e8: float32be_t
    40 88 00 00
address = 0x7ff7b3e145e4: float32le_t
    00 00 88 40
address = 0x7ff7b3e145d0: float64be_t
    40 20 40 00 00 00 00 00
address = 0x7ff7b3e145d8: float64le_t
    00 00 00 00 00 40 20 40

address = 0x7ff7b3e14630: i16be_t
    00 2a
address = 0x7ff7b3e14636: i16le_t
    2a 00
address = 0x7ff7b3e14620: u16be_t
    00 20
address = 0x7ff7b3e1461e: u16le_t
    20 00
address = 0x7ff7b3e14628: i32be_t
    00 00 00 2c
address = 0x7ff7b3e14600: i32le_t
    2c 00 00 00
address = 0x7ff7b3e14618: u32be_t
    00 00 00 22
address = 0x7ff7b3e14604: u32le_t
    22 00 00 00
address = 0x7ff7b3e14610: i64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7b3e14608: i64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7b3e145f8: u64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7b3e145f0: u64le_t
    26 00 00 00 00 00 00 00

address = 0x7ff7b3e14630: i16be_t
    02 a0
address = 0x7ff7b3e14636: i16le_t
    a0 02
address = 0x7ff7b3e14620: u16be_t
    02 00
address = 0x7ff7b3e1461e: u16le_t
    00 02
address = 0x7ff7b3e14628: i32be_t
    00 00 02 c0
address = 0x7ff7b3e14600: i32le_t
    c0 02 00 00
address = 0x7ff7b3e14618: u32be_t
    00 00 02 20
address = 0x7ff7b3e14604: u32le_t
    20 02 00 00
address = 0x7ff7b3e14610: i64be_t
    00 00 00 00 00 00 03 00
address = 0x7ff7b3e14608: i64le_t
    00 03 00 00 00 00 00 00
address = 0x7ff7b3e145f8: u64be_t
    00 00 00 00 00 00 02 60
address = 0x7ff7b3e145f0: u64le_t
    60 02 00 00 00 00 00 00

compare:
    i16be(672) <  i16le(672) : false
    i16be(672) <= i16le(672) : true
    i16be(672) == i16le(672) : true
    i16be(672) != i16le(672) : false
    i16be(672) >= i16le(672) : true
    i16be(672) >  i16le(672) : false

    i32be(704) <  i16le(672) : false
    i32be(704) <= i16le(672) : false
    i32be(704) == i16le(672) : false
    i32be(704) != i16le(672) : true
    i32be(704) >= i16le(672) : true
    i32be(704) >  i16le(672) : true

    i32be(704) <  i64be(768) : true
    i32be(704) <= i64be(768) : true
    i32be(704) == i64be(768) : false
    i32be(704) != i64be(768) : true
    i32be(704) >= i64be(768) : false
    i32be(704) >  i64be(768) : false

    i32be(704) <  i64le(768) : true
    i32be(704) <= i64le(768) : true
    i32be(704) == i64le(768) : false
    i32be(704) != i64le(768) : true
    i32be(704) >= i64le(768) : false
    i32be(704) >  i64le(768) : false

typeid:
    int8_t:a  int16_t:s  int32_t:i  int64_t:x
    uint8_t:h  uint16_t:t  uint32_t:j  uint64_t:y
    float:f  double:d
    char:c  wchar_t:w
    int:i  unsigned:j
    sizeof(wchar_t):4

int16be_t = 14byteswap_valueIsE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:i  +wchar:i

int16le_t = s
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:i  +wchar:i

int32be_t = 14byteswap_valueIiE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:i  +wchar:i

int32le_t = i
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:i  +wchar:i

int64be_t = 14byteswap_valueIxE
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +f32:f  +f64:d
    +char:x  +wchar:x

int64le_t = x
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +f32:f  +f64:d
    +char:x  +wchar:x

uint16be_t = 14byteswap_valueItE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:i  +wchar:i

uint16le_t = t
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:i  +wchar:i

uint32be_t = 14byteswap_valueIjE
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:j  +wchar:j

uint32le_t = j
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +f32:f  +f64:d
    +char:j  +wchar:j

uint64be_t = 14byteswap_valueIyE
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +f32:f  +f64:d
    +char:y  +wchar:y

uint64le_t = y
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +f32:f  +f64:d
    +char:y  +wchar:y

float32be_t = 14byteswap_valueIfE
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +f32:f  +f64:d
    +char:f  +wchar:f

float32le_t = f
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +f32:f  +f64:d
    +char:f  +wchar:f

float64be_t = 14byteswap_valueIdE
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +f32:d  +f64:d
    +char:d  +wchar:d

float64le_t = d
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +f32:d  +f64:d
    +char:d  +wchar:d
```

</div></details>


## C++11 で書いてみる

```C++:byteswap_value型
template <typename T>
class byteswap_value
{
public:
    using value_type = T;  // byteswap する native の型.

protected:

    /* 逆バイト順型 */
    template <typename I> struct bswap { using type = I; };
    template <> struct bswap<float> { using type = std::uint32_t; };
    template <> struct bswap<double> { using type = std::uint64_t; };

    using bswap_value = typename bswap<T>::type;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline constexpr bswap_cast(const T& n) noexcept : v(n) {}
        inline constexpr operator bswap_value() const noexcept
            { return std::byteswap(*(const bswap_value*)&v); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline constexpr native_cast(const bswap_value& s) noexcept : v(std::byteswap(s)) {}
        inline constexpr operator T() const noexcept { return *(const T*)&v; }
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    byteswap_value() noexcept = default;
    inline constexpr byteswap_value(T d) noexcept : _v(bswap_cast(d)) {}
    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;

    inline constexpr operator T() const noexcept { return get(); }

    inline constexpr bool operator !() const noexcept { return !_v; }
    inline constexpr bool operator ==(const byteswap_value<T>& s) const noexcept { return _v == s._v; }
    inline constexpr bool operator !=(const byteswap_value<T>& s) const noexcept { return _v != s._v; }

    template <typename U> inline constexpr bool operator < (U n) const noexcept { return get() <  n; }
    template <typename U> inline constexpr bool operator <=(U n) const noexcept { return get() <= n; }
    template <typename U> inline constexpr bool operator ==(U n) const noexcept { return get() == n; }
    template <typename U> inline constexpr bool operator !=(U n) const noexcept { return get() != n; }
    template <typename U> inline constexpr bool operator >=(U n) const noexcept { return get() >= n; }
    template <typename U> inline constexpr bool operator > (U n) const noexcept { return get() >  n; }

    inline constexpr T operator +() const noexcept { return +get(); }
    inline constexpr T operator -() const noexcept { return -get(); }
    inline constexpr T operator ~() const noexcept { return ~get(); }

    template <typename U> inline constexpr decltype(T() + U()) operator +(U n) const noexcept { return get() + n; }
    template <typename U> inline constexpr decltype(T() - U()) operator -(U n) const noexcept { return get() - n; }
    template <typename U> inline constexpr decltype(T() * U()) operator *(U n) const noexcept { return get() * n; }
    template <typename U> inline constexpr decltype(T() / U()) operator /(U n) const noexcept { return get() / n; }
    template <typename U> inline constexpr decltype(T() % U()) operator %(U n) const noexcept { return get() % n; }

    template <typename U> inline constexpr decltype(T() & U()) operator &(U n) const noexcept { return get() & n; }
    template <typename U> inline constexpr decltype(T() | U()) operator |(U n) const noexcept { return get() | n; }
    template <typename U> inline constexpr decltype(T() ^ U()) operator ^(U n) const noexcept { return get() ^ n; }

    inline constexpr T operator <<(int n) const noexcept { return get() << n; }
    inline constexpr T operator >>(int n) const noexcept { return get() >> n; }

    inline byteswap_value<T>& operator =(T n) { return set(n); }
    byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;

    template <typename U> inline byteswap_value<T>& operator +=(U n) noexcept { return set(get() + n); }
    template <typename U> inline byteswap_value<T>& operator -=(U n) noexcept { return set(get() - n); }
    template <typename U> inline byteswap_value<T>& operator *=(U n) noexcept { return set(get() * n); }
    template <typename U> inline byteswap_value<T>& operator /=(U n) noexcept { return set(get() / n); }
    template <typename U> inline byteswap_value<T>& operator %=(U n) noexcept { return set(get() % n); }

    inline byteswap_value<T>& operator &=(T n) noexcept { return set(get() & n); }
    inline byteswap_value<T>& operator |=(T n) noexcept { return set(get() | n); }
    inline byteswap_value<T>& operator ^=(T n) noexcept { return set(get() ^ n); }

    inline byteswap_value<T>& operator <<=(int n) noexcept { return set(get() << n); }
    inline byteswap_value<T>& operator >>=(int n) noexcept { return set(get() >> n); }

protected:
    inline constexpr T get() const noexcept { return native_cast(_v); }
    inline byteswap_value<T>& set(T n) noexcept { _v = bswap_cast(n); return *this; }
};
```

### C++03 からの変更

#### trivial 型になった

- コンストラクタとコピー代入演算子を <code>= default</code> に変更。

```diff:更新差分(抜粋)
-    inline byteswap_value() {}
-    inline byteswap_value(const byteswap_value<T>& s) : _v(s._v) {}
-    inline byteswap_value<T>& operator =(const byteswap_value<T>& s) { _v = s._v; return *this; }
+    byteswap_value() noexcept = default;
+    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;
+    byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;
```

#### 演算子の返り値型を <code>decltyle</code> で置換

```diff:更新差分(抜粋)
-    template <typename U> inline typename retval<U>::type operator +(U n) const { return get() + n; }
+    template <typename U> inline constexpr decltype(T() + U()) operator +(U n) const noexcept { return get() + n; }
```

加えて、自前の型推論の関連記述を削除。

#### constexpr 修飾子の使用

<code>constexpr</code> のメンバ関数に<code>const</code>を[付けられないらしい](https://cpprefjp.github.io/lang/cpp14/relaxing_constraints_on_constexpr.html)が、<code>const</code>を外して<code>clang++ -std=c++11</code>でコンパイルすると警告が出るので付けておく。

```console:
warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Wconstexpr-not-const]
```

##### 最適化するとエンディアン値が直接配置される

最適化しないとコンストラクタが呼ばれるが、最適化して値が確定する場合はコンストラクタが呼ばれず直接配置してくれる。

```C++:テストプログラムより
static const uint32_t native_data_u32[] = { 0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210 };
static const uint32be_t big_data_u32[] = { 0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210 };
static const uint32le_t little_data_u32[] = { 0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210 };
```

```assembly:アセンブラ出力(clang++)
__ZL15native_data_u32:
	.long	19088743                        ## 0x1234567
	.long	2309737967                      ## 0x89abcdef
	.long	4275878552                      ## 0xfedcba98
	.long	1985229328                      ## 0x76543210
〜〜〜(略)〜〜〜
__ZL12big_data_u32:
	.long	1732584193                      ## 0x67452301
	.long	4023233417                      ## 0xefcdab89
	.long	2562383102                      ## 0x98badcfe
	.long	271733878                       ## 0x10325476
〜〜〜(略)〜〜〜
__ZL15little_data_u32:
	.long	19088743                        ## 0x1234567
	.long	2309737967                      ## 0x89abcdef
	.long	4275878552                      ## 0xfedcba98
	.long	1985229328                      ## 0x76543210
```


#### std::basic_string が利用可能

```C++:std::basic_stringの利用
using wstringbe = std::basic_string<wcharbe_t>;
using wstringle = std::basic_string<wcharle_t>;
using u16stringbe = std::basic_string<char16be_t>;
using u16stringle = std::basic_string<char16le_t>;
using u32stringbe = std::basic_string<char32be_t>;
using u32stringle = std::basic_string<char32le_t>;
```

サロゲートペア文字の変換は行われませんが

```C++:BMP文字限定
std::wstring ws(L"サンプルTEXT");  // UTF-16/32
u16stringbe u16sbe(u16s.begin(), u16s.end());   // UTF-16BE
u16stringle u16sle(u16s.begin(), u16s.end());   // UTF-16LE
```

とすることができます。


#### その他

- <code>typedef</code> から <code>using</code> に変更
- <code>noexcept</code> 修飾を使用
- <code>char16_t, char32_t</code> に対する定義を追加(各エンディアン型)


### テスト プログラム

<details><summary>endian-11.cpp (やや長いので折りたたみ)</summary><div>

```C++:endian-11.cpp
#include "endian.hpp"
#include "byteswap.hpp"

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

#include <cstdint>
#include <limits>

/*
 * byteswap_value
 */
template <typename T>
class byteswap_value
{
public:
    using value_type = T;  // byteswap する native の型.

protected:

    /* 逆バイト順型 */
    template <typename I> struct bswap { using type = I; };
    template <> struct bswap<float> { using type = std::uint32_t; };
    template <> struct bswap<double> { using type = std::uint64_t; };

    using bswap_value = typename bswap<T>::type;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline constexpr bswap_cast(const T& n) noexcept : v(n) {}
        inline constexpr operator bswap_value() const noexcept
            { return std::byteswap(*(const bswap_value*)&v); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline constexpr native_cast(const bswap_value& s) noexcept : v(std::byteswap(s)) {}
        inline constexpr operator T() const noexcept { return *(const T*)&v; }
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    byteswap_value() noexcept = default;
    inline constexpr byteswap_value(T d) noexcept : _v(bswap_cast(d)) {}
    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;

    inline constexpr operator T() const noexcept { return get(); }

    inline constexpr bool operator !() const noexcept { return !_v; }
    inline constexpr bool operator ==(const byteswap_value<T>& s) const noexcept { return _v == s._v; }
    inline constexpr bool operator !=(const byteswap_value<T>& s) const noexcept { return _v != s._v; }

    template <typename U> inline constexpr bool operator < (U n) const noexcept { return get() <  n; }
    template <typename U> inline constexpr bool operator <=(U n) const noexcept { return get() <= n; }
    template <typename U> inline constexpr bool operator ==(U n) const noexcept { return get() == n; }
    template <typename U> inline constexpr bool operator !=(U n) const noexcept { return get() != n; }
    template <typename U> inline constexpr bool operator >=(U n) const noexcept { return get() >= n; }
    template <typename U> inline constexpr bool operator > (U n) const noexcept { return get() >  n; }

    inline constexpr T operator +() const noexcept { return +get(); }
    inline constexpr T operator -() const noexcept { return -get(); }
    inline constexpr T operator ~() const noexcept { return ~get(); }

    template <typename U> inline constexpr decltype(T() + U()) operator +(U n) const noexcept { return get() + n; }
    template <typename U> inline constexpr decltype(T() - U()) operator -(U n) const noexcept { return get() - n; }
    template <typename U> inline constexpr decltype(T() * U()) operator *(U n) const noexcept { return get() * n; }
    template <typename U> inline constexpr decltype(T() / U()) operator /(U n) const noexcept { return get() / n; }
    template <typename U> inline constexpr decltype(T() % U()) operator %(U n) const noexcept { return get() % n; }

    template <typename U> inline constexpr decltype(T() & U()) operator &(U n) const noexcept { return get() & n; }
    template <typename U> inline constexpr decltype(T() | U()) operator |(U n) const noexcept { return get() | n; }
    template <typename U> inline constexpr decltype(T() ^ U()) operator ^(U n) const noexcept { return get() ^ n; }

    inline constexpr T operator <<(int n) const noexcept { return get() << n; }
    inline constexpr T operator >>(int n) const noexcept { return get() >> n; }

    inline byteswap_value<T>& operator =(T n) { return set(n); }
    byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;

    template <typename U> inline byteswap_value<T>& operator +=(U n) noexcept { return set(get() + n); }
    template <typename U> inline byteswap_value<T>& operator -=(U n) noexcept { return set(get() - n); }
    template <typename U> inline byteswap_value<T>& operator *=(U n) noexcept { return set(get() * n); }
    template <typename U> inline byteswap_value<T>& operator /=(U n) noexcept { return set(get() / n); }
    template <typename U> inline byteswap_value<T>& operator %=(U n) noexcept { return set(get() % n); }

    inline byteswap_value<T>& operator &=(T n) noexcept { return set(get() & n); }
    inline byteswap_value<T>& operator |=(T n) noexcept { return set(get() | n); }
    inline byteswap_value<T>& operator ^=(T n) noexcept { return set(get() ^ n); }

    inline byteswap_value<T>& operator <<=(int n) noexcept { return set(get() << n); }
    inline byteswap_value<T>& operator >>=(int n) noexcept { return set(get() >> n); }

protected:
    inline constexpr T get() const noexcept { return native_cast(_v); }
    inline byteswap_value<T>& set(T n) noexcept { _v = bswap_cast(n); return *this; }
};

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

#include <string>

struct native_endian
{
    enum {
        is_big = std::endian::big == std::endian::native,
        is_little = std::endian::little == std::endian::native,
    };

    using int8_type = std::int8_t;
    using int16_type = std::int16_t;
    using int32_type = std::int32_t;
    using int64_type = std::int64_t;

    using uint8_type = std::uint8_t;
    using uint16_type = std::uint16_t;
    using uint32_type = std::uint32_t;
    using uint64_type = std::uint64_t;

    using float32_type = float;
    using float64_type = double;

    using char_type = char;
    using wchar_type = wchar_t;
    using char16_type = char16_t;
    using char32_type = char32_t;
};

struct byteswap_endian
{
    enum {
        is_big = !native_endian::is_big,
        is_little = !native_endian::is_little,
    };

    using int8_type = native_endian::int8_type;
    using int16_type = byteswap_value<native_endian::int16_type>;
    using int32_type = byteswap_value<native_endian::int32_type>;
    using int64_type = byteswap_value<native_endian::int64_type>;

    using uint8_type = native_endian::uint8_type;
    using uint16_type = byteswap_value<native_endian::uint16_type>;
    using uint32_type = byteswap_value<native_endian::uint32_type>;
    using uint64_type = byteswap_value<native_endian::uint64_type>;

    using float32_type = byteswap_value<native_endian::float32_type>;
    using float64_type = byteswap_value<native_endian::float64_type>;

    using char_type = byteswap_value<native_endian::char_type>;
    using wchar_type = byteswap_value<native_endian::wchar_type>;
    using char16_type = byteswap_value<native_endian::char16_type>;
    using char32_type = byteswap_value<native_endian::char32_type>;
};

template <bool big>
using fixed_endian = typename std::conditional<
    big == native_endian::is_big, native_endian, byteswap_endian>::type;

/* big endian */

using big_endian = fixed_endian<true>;

using int8be_t = big_endian::int8_type;
using int16be_t = big_endian::int16_type;
using int32be_t = big_endian::int32_type;
using int64be_t = big_endian::int64_type;

using uint8be_t = big_endian::uint8_type;
using uint16be_t = big_endian::uint16_type;
using uint32be_t = big_endian::uint32_type;
using uint64be_t = big_endian::uint64_type;

using float32be_t = big_endian::float32_type;
using float64be_t = big_endian::float64_type;

using charbe_t = big_endian::char_type;
using wcharbe_t = big_endian::wchar_type;
using char16be_t = big_endian::char16_type;
using char32be_t = big_endian::char32_type;

using stringbe = std::basic_string<charbe_t>;
using wstringbe = std::basic_string<wcharbe_t>;
using u16stringbe = std::basic_string<char16be_t>;
using u32stringbe = std::basic_string<char32be_t>;


/* little endian */

using little_endian = fixed_endian<false>;

using int8le_t = little_endian::int8_type;
using int16le_t = little_endian::int16_type;
using int32le_t = little_endian::int32_type;
using int64le_t = little_endian::int64_type;

using uint8le_t = little_endian::uint8_type;
using uint16le_t = little_endian::uint16_type;
using uint32le_t = little_endian::uint32_type;
using uint64le_t = little_endian::uint64_type;

using float32le_t = little_endian::float32_type;
using float64le_t = little_endian::float64_type;

using charle_t = little_endian::char_type;
using wcharle_t = little_endian::wchar_type;
using char16le_t = little_endian::char16_type;
using char32le_t = little_endian::char32_type;

using stringle = std::basic_string<charle_t>;
using wstringle = std::basic_string<wcharle_t>;
using u16stringle = std::basic_string<char16le_t>;
using u32stringle = std::basic_string<char32le_t>;


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

/*
 * テスト
 */

#include <algorithm>
#include <cstdio>
#include <typeinfo>

static const uint16_t native_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static const uint16be_t big_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static const uint16le_t little_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};

static const uint32_t native_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static const uint32be_t big_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static const uint32le_t little_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};

static const uint64_t native_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static const uint64be_t big_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static const uint64le_t little_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};

static const float native_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32be_t big_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32le_t little_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};

static const double native_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64be_t big_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64le_t little_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};

static void dumpbin(const char* msg, const void* s, size_t n)
{
    const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
    size_t x, y, ny = n & ~15, nr = n & 15;

    printf("address = %p: %s\n", s, msg);
    for (y = 0; y < ny; y += 16)
    {
        printf("   ");
        for (size_t x = 0; x < 16; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
    if (y < n)
    {
        printf("   ");
        for (x = 0; x < nr; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
}

template <typename T>
inline static void dumpbin(const char* msg, const T& s)
{
    dumpbin(msg, &s, sizeof(T));
}

template <typename T>
inline static void dumpbin(const char* msg, const std::basic_string<T>& s)
{
    dumpbin(msg, s.data(), s.size() * sizeof(T));
}

template <typename T>
inline static const char *gettypename()
{
    return typeid(T()).name();
}

template <typename T>
inline static const char *gettypename(const T v)
{
    return typeid(v).name();
}

template <typename T>
inline static const char *valuetypename()
{
    return typeid(T(0)).name();
}

template <typename T>
static void rtypename(const char* name, const T& x)
{

    int8_t i8 = 11;
    int16_t i16 = 12;
    int32_t i32 = 14;
    int64_t i64 = 18;
    uint8_t u8 = 21;
    uint16_t u16 = 22;
    uint32_t u32 = 24;
    uint64_t u64 = 28;
    float f32 = 1.5;
    double f64 = 2.25;

    char c8 = 1;
    wchar_t wc = 2;
    char16_t c16 = 2;
    char32_t c32 = 4;

    printf("%s = %s\n", name, gettypename(x));
    printf("    +i8:%s  +i16:%s  +i32:%s  +i64:%s\n",
           gettypename(x + i8), gettypename(x + i16),
           gettypename(x + i32), gettypename(x + i64));
    printf("    +u8:%s  +u16:%s  +u32:%s  +u64:%s\n",
           gettypename(x + u8), gettypename(x + u16),
           gettypename(x + u32), gettypename(x + u64));
    printf("    +c8:%s  +c16:%s  +c32:%s  +wc:%s\n",
           gettypename(x + c8), gettypename(x + c16),
           gettypename(x + c32), gettypename(x + wc));
    printf("    +f32:%s  +f64:%s\n",
           gettypename(x + f32), gettypename(x + f64));
    printf("\n");

#if 0
    T y = 0;

    printf("    +=i8:%s  +=i16:%s  +=i32:%s  +i64:%s\n",
           gettypename(y += i8), gettypename(y += i16),
           gettypename(y += i32), gettypename(y += i64));
    printf("    +=u8:%s  +=u16:%s  +=u32:%s  +=u64:%s\n",
           gettypename(y += u8), gettypename(y += u16),
           gettypename(y += u32), gettypename(y += u64));
    printf("    +=f32:%s  +=f64:%s\n",
           gettypename(y += f32), gettypename(y += f64));
    printf("    +=c8:%s  +=wc:%s\n",
           gettypename(y += c8), gettypename(y += wc));
    printf("\n");
#endif
}

/*
 *
 */

int main()
{
#ifdef __cplusplus
    printf("compiler: __cplusplus = %ld\n", __cplusplus);
#endif
#ifdef _MSVC_LANG
    printf("compiler: _MSVC_LANG = %ld\n", _MSVC_LANG);
#endif

    printf("endian: native = %s\n", native_endian::is_big ? "big" : "little");
    printf("\n");

    dumpbin("native_data_u16", native_data_u16);
    dumpbin("big_data_u16", big_data_u16);
    dumpbin("little_data_u16", little_data_u16);
    printf("\n");

    dumpbin("native_data_u32", native_data_u32);
    dumpbin("big_data_u32", big_data_u32);
    dumpbin("little_data_u32", little_data_u32);
    printf("\n");

    dumpbin("native_data_u64", native_data_u64);
    dumpbin("big_data_u64", big_data_u64);
    dumpbin("little_data_u64", little_data_u64);
    printf("\n");

    dumpbin("native_data_f32", native_data_f32);
    dumpbin("big_data_f32", big_data_f32);
    dumpbin("little_data_f32", little_data_f32);
    printf("\n");

    dumpbin("native_data_f64", native_data_f64);
    dumpbin("big_data_f64", big_data_f64);
    dumpbin("little_data_f64", little_data_f64);
    printf("\n");

    int16be_t i16be = 32;
    int32be_t i32be = 34;
    int64be_t i64be = 38;
    uint16be_t u16be = 42;
    uint32be_t u32be = 44;
    uint64be_t u64be = 48;
    float32be_t f32be = 4.25;
    float64be_t f64be = 8.125;

    int16le_t i16le = i16be;
    int32le_t i32le = i32be;
    int64le_t i64le = i64be;
    uint16le_t u16le = u16be;
    uint32le_t u32le = u32be;
    uint64le_t u64le = u64be;
    float32le_t f32le = f32be;
    float64le_t f64le = f64be;

    /**/

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    dumpbin("float32be_t", f32be); dumpbin("float32le_t", f32le);
    dumpbin("float64be_t", f64be); dumpbin("float64le_t", f64le);
    printf("\n");

    /**/

    u16be = 32; u32be = 34; u64be = 38;
    i16be = 42; i32be = 44; i64be = 48;
    u16le = 32; u32le = 34; u64le = 38;
    i16le = 42; i32le = 44; i64le = 48;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    u16be <<= 4; u32be <<= 4; u64be <<= 4;
    i16be <<= 4; i32be <<= 4; i64be <<= 4;
    u16le <<= 4; u32le <<= 4; u64le <<= 4;
    i16le <<= 4; i32le <<= 4; i64le <<= 4;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    printf("compare:\n");
#define COMP(l,op,r)                            \
    printf("    %s(%d) %-2s %s(%d) : %s\n",     \
           #l, (int)l, #op, #r, (int)r,         \
           (l op r) ? "true" : "false")
    COMP(i16be, < , i16le); COMP(i16be, <=, i16le); COMP(i16be, ==, i16le);
    COMP(i16be, !=, i16le); COMP(i16be, >=, i16le); COMP(i16be, > , i16le);
    printf("\n");
    COMP(i32be, < , i16le); COMP(i32be, <=, i16le); COMP(i32be, ==, i16le);
    COMP(i32be, !=, i16le); COMP(i32be, >=, i16le); COMP(i32be, > , i16le);
    printf("\n");
    COMP(i32be, < , i64be); COMP(i32be, <=, i64be); COMP(i32be, ==, i64be);
    COMP(i32be, !=, i64be); COMP(i32be, >=, i64be); COMP(i32be, > , i64be);
    printf("\n");
    COMP(i32be, < , i64le); COMP(i32be, <=, i64le); COMP(i32be, ==, i64le);
    COMP(i32be, !=, i64le); COMP(i32be, >=, i64le); COMP(i32be, > , i64le);
    printf("\n");

    /**/

    printf("typeid:\n");
    printf("    int8_t:%s  int16_t:%s  int32_t:%s  int64_t:%s\n",
           valuetypename<int8_t>(), valuetypename<int16_t>(),
           valuetypename<int32_t>(), valuetypename<int64_t>());
    printf("    uint8_t:%s  uint16_t:%s  uint32_t:%s  uint64_t:%s\n",
           valuetypename<uint8_t>(), valuetypename<uint16_t>(),
           valuetypename<uint32_t>(), valuetypename<uint64_t>());
    printf("    float:%s  double:%s\n", valuetypename<float>(), valuetypename<double>());
    printf("    char:%s  wchar_t:%s\n", valuetypename<char>(), valuetypename<wchar_t>());
    printf("    int:%s  unsigned:%s\n", valuetypename<int>(), valuetypename<unsigned>());
    printf("    sizeof(wchar_t):%zd\n", sizeof(wchar_t));
    printf("\n");

    /**/

    rtypename("int16be_t", i16be); rtypename("int16le_t", i16le);
    rtypename("int32be_t", i32be); rtypename("int32le_t", i32le);
    rtypename("int64be_t", i64be); rtypename("int64le_t", i64le);

    rtypename("uint16be_t", u16be); rtypename("uint16le_t", u16le);
    rtypename("uint32be_t", u32be); rtypename("uint32le_t", u32le);
    rtypename("uint64be_t", u64be); rtypename("uint64le_t", u64le);

    rtypename("float32be_t", f32be); rtypename("float32le_t", f32le);
    rtypename("float64be_t", f64be); rtypename("float64le_t", f64le);

    /**/

    std::wstring ws(L"サンプルTEXT");
    wstringbe wsbe(ws.begin(), ws.end());
    wstringle wsle(ws.begin(), ws.end());
    dumpbin("wstring", ws);
    dumpbin("wstringbe", wsbe);
    dumpbin("wstringle", wsle);
    printf("\n");

    std::u16string u16s(ws.begin(), ws.end()); /* sizeof(wchar_t) != sizeof(char16_t) の場合は要注意 */
    u16stringbe u16sbe(u16s.begin(), u16s.end());
    u16stringle u16sle(u16s.begin(), u16s.end());
    dumpbin("u16string", u16s);
    dumpbin("u16stringbe", u16sbe);
    dumpbin("u16stringle", u16sle);
    printf("\n");

    std::u32string u32s(ws.begin(), ws.end()); /* sizeof(wchar_t) != sizeof(char32_t) の場合は要注意 */
    u32stringbe u32sbe(u32s.begin(), u32s.end());
    u32stringle u32sle(u32s.begin(), u32s.end());
    dumpbin("u32string", u32s);
    dumpbin("u32stringbe", u32sbe);
    dumpbin("u32stringle", u32sle);
    printf("\n");

    return 0;
}
```

```console:実行結果
$ clang++ -O3 -g0 -DNDEBUG=1 -std=c++11 endian-11.cpp -o endian-11
$ ./endian-11
compiler: __cplusplus = 201103
endian: native = little

address = 0x10f5c2900: native_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32
address = 0x10f5c2910: big_data_u16
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10f5c2920: little_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32

address = 0x10f5c2930: native_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76
address = 0x10f5c2940: big_data_u32
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10f5c2950: little_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76

address = 0x10f5c2960: native_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe
address = 0x10f5c2970: big_data_u64
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10f5c2980: little_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe

address = 0x10f5c2990: native_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76
address = 0x10f5c4000: big_data_f32
    34 56 78 9a 45 67 89 ab 56 78 9a bc 76 54 32 10
address = 0x10f5c4030: little_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76

address = 0x10f5c29a0: native_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76
address = 0x10f5c4010: big_data_f64
    34 56 78 9a bc de f0 12 45 67 89 ab cd ef 01 23
    56 78 9a bc de f0 12 34 76 54 32 10 fe dc ba 98
address = 0x10f5c4040: little_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76

address = 0x7ff7b0941630: i16be_t
    00 20
address = 0x7ff7b0941636: i16le_t
    20 00
address = 0x7ff7b0941620: u16be_t
    00 2a
address = 0x7ff7b094161e: u16le_t
    2a 00
address = 0x7ff7b0941628: i32be_t
    00 00 00 22
address = 0x7ff7b0941600: i32le_t
    22 00 00 00
address = 0x7ff7b0941618: u32be_t
    00 00 00 2c
address = 0x7ff7b0941604: u32le_t
    2c 00 00 00
address = 0x7ff7b0941610: i64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7b0941608: i64le_t
    26 00 00 00 00 00 00 00
address = 0x7ff7b09415f8: u64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7b09415f0: u64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7b09415e8: float32be_t
    40 88 00 00
address = 0x7ff7b09415e4: float32le_t
    00 00 88 40
address = 0x7ff7b09415d0: float64be_t
    40 20 40 00 00 00 00 00
address = 0x7ff7b09415d8: float64le_t
    00 00 00 00 00 40 20 40

address = 0x7ff7b0941630: i16be_t
    00 2a
address = 0x7ff7b0941636: i16le_t
    2a 00
address = 0x7ff7b0941620: u16be_t
    00 20
address = 0x7ff7b094161e: u16le_t
    20 00
address = 0x7ff7b0941628: i32be_t
    00 00 00 2c
address = 0x7ff7b0941600: i32le_t
    2c 00 00 00
address = 0x7ff7b0941618: u32be_t
    00 00 00 22
address = 0x7ff7b0941604: u32le_t
    22 00 00 00
address = 0x7ff7b0941610: i64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7b0941608: i64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7b09415f8: u64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7b09415f0: u64le_t
    26 00 00 00 00 00 00 00

address = 0x7ff7b0941630: i16be_t
    02 a0
address = 0x7ff7b0941636: i16le_t
    a0 02
address = 0x7ff7b0941620: u16be_t
    02 00
address = 0x7ff7b094161e: u16le_t
    00 02
address = 0x7ff7b0941628: i32be_t
    00 00 02 c0
address = 0x7ff7b0941600: i32le_t
    c0 02 00 00
address = 0x7ff7b0941618: u32be_t
    00 00 02 20
address = 0x7ff7b0941604: u32le_t
    20 02 00 00
address = 0x7ff7b0941610: i64be_t
    00 00 00 00 00 00 03 00
address = 0x7ff7b0941608: i64le_t
    00 03 00 00 00 00 00 00
address = 0x7ff7b09415f8: u64be_t
    00 00 00 00 00 00 02 60
address = 0x7ff7b09415f0: u64le_t
    60 02 00 00 00 00 00 00

compare:
    i16be(672) <  i16le(672) : false
    i16be(672) <= i16le(672) : true
    i16be(672) == i16le(672) : true
    i16be(672) != i16le(672) : false
    i16be(672) >= i16le(672) : true
    i16be(672) >  i16le(672) : false

    i32be(704) <  i16le(672) : false
    i32be(704) <= i16le(672) : false
    i32be(704) == i16le(672) : false
    i32be(704) != i16le(672) : true
    i32be(704) >= i16le(672) : true
    i32be(704) >  i16le(672) : true

    i32be(704) <  i64be(768) : true
    i32be(704) <= i64be(768) : true
    i32be(704) == i64be(768) : false
    i32be(704) != i64be(768) : true
    i32be(704) >= i64be(768) : false
    i32be(704) >  i64be(768) : false

    i32be(704) <  i64le(768) : true
    i32be(704) <= i64le(768) : true
    i32be(704) == i64le(768) : false
    i32be(704) != i64le(768) : true
    i32be(704) >= i64le(768) : false
    i32be(704) >  i64le(768) : false

typeid:
    int8_t:a  int16_t:s  int32_t:i  int64_t:x
    uint8_t:h  uint16_t:t  uint32_t:j  uint64_t:y
    float:f  double:d
    char:c  wchar_t:w
    int:i  unsigned:j
    sizeof(wchar_t):4

int16be_t = 14byteswap_valueIsE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int16le_t = s
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int32be_t = 14byteswap_valueIiE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int32le_t = i
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int64be_t = 14byteswap_valueIxE
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +c8:x  +c16:x  +c32:x  +wc:x
    +f32:f  +f64:d

int64le_t = x
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +c8:x  +c16:x  +c32:x  +wc:x
    +f32:f  +f64:d

uint16be_t = 14byteswap_valueItE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

uint16le_t = t
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

uint32be_t = 14byteswap_valueIjE
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +c8:j  +c16:j  +c32:j  +wc:j
    +f32:f  +f64:d

uint32le_t = j
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +c8:j  +c16:j  +c32:j  +wc:j
    +f32:f  +f64:d

uint64be_t = 14byteswap_valueIyE
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +c8:y  +c16:y  +c32:y  +wc:y
    +f32:f  +f64:d

uint64le_t = y
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +c8:y  +c16:y  +c32:y  +wc:y
    +f32:f  +f64:d

float32be_t = 14byteswap_valueIfE
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +c8:f  +c16:f  +c32:f  +wc:f
    +f32:f  +f64:d

float32le_t = f
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +c8:f  +c16:f  +c32:f  +wc:f
    +f32:f  +f64:d

float64be_t = 14byteswap_valueIdE
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +c8:d  +c16:d  +c32:d  +wc:d
    +f32:d  +f64:d

float64le_t = d
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +c8:d  +c16:d  +c32:d  +wc:d
    +f32:d  +f64:d

address = 0x600001cbc000: wstring
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
address = 0x600001cbc030: wstringbe
    00 00 30 b5 00 00 30 f3 00 00 30 d7 00 00 30 eb
    00 00 00 54 00 00 00 45 00 00 00 58 00 00 00 54
address = 0x600001cbc060: wstringle
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00

address = 0x7ff7b094159a: u16string
    b5 30 f3 30 d7 30 eb 30 54 00 45 00 58 00 54 00
address = 0x7ff7b0941552: u16stringbe
    30 b5 30 f3 30 d7 30 eb 00 54 00 45 00 58 00 54
address = 0x7ff7b094156a: u16stringle
    b5 30 f3 30 d7 30 eb 30 54 00 45 00 58 00 54 00

address = 0x600001cb0000: u32string
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
address = 0x600001cb0030: u32stringbe
    00 00 30 b5 00 00 30 f3 00 00 30 d7 00 00 30 eb
    00 00 00 54 00 00 00 45 00 00 00 58 00 00 00 54
address = 0x600001cb0060: u32stringle
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
```

</div></details>


## C++14 で書いてみる

```C++:byteswap_value型
template <typename T>
class byteswap_value
{
public:
    using value_type = T;  // byteswap する native の型.

protected:

    /* 逆バイト順型 */
    template <typename I> struct bswap { using type = I; };
    template <> struct bswap<float> { using type = std::uint32_t; };
    template <> struct bswap<double> { using type = std::uint64_t; };

    using bswap_value = typename bswap<T>::type;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline constexpr bswap_cast(const T& n) noexcept : v(n) {}
        inline constexpr operator bswap_value() const noexcept
            { return std::byteswap(*(const bswap_value*)&v); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline constexpr native_cast(const bswap_value& s) noexcept : v(std::byteswap(s)) {}
        inline constexpr operator T() const noexcept { return *(const T*)&v; }
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    byteswap_value() noexcept = default;
    inline constexpr byteswap_value(T d) noexcept : _v(bswap_cast(d)) {}
    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;

    inline constexpr operator T() const noexcept { return get(); }

    inline constexpr bool operator !() const noexcept { return !_v; }
    inline constexpr bool operator ==(const byteswap_value<T>& s) const noexcept { return _v == s._v; }
    inline constexpr bool operator !=(const byteswap_value<T>& s) const noexcept { return _v != s._v; }

    template <typename U> inline constexpr bool operator < (U n) const noexcept { return get() <  n; }
    template <typename U> inline constexpr bool operator <=(U n) const noexcept { return get() <= n; }
    template <typename U> inline constexpr bool operator ==(U n) const noexcept { return get() == n; }
    template <typename U> inline constexpr bool operator !=(U n) const noexcept { return get() != n; }
    template <typename U> inline constexpr bool operator >=(U n) const noexcept { return get() >= n; }
    template <typename U> inline constexpr bool operator > (U n) const noexcept { return get() >  n; }

    inline constexpr T operator +() const noexcept { return +get(); }
    inline constexpr T operator -() const noexcept { return -get(); }
    inline constexpr T operator ~() const noexcept { return ~get(); }

    template <typename U> inline constexpr auto operator +(U n) const noexcept { return get() + n; }
    template <typename U> inline constexpr auto operator -(U n) const noexcept { return get() - n; }
    template <typename U> inline constexpr auto operator *(U n) const noexcept { return get() * n; }
    template <typename U> inline constexpr auto operator /(U n) const noexcept { return get() / n; }
    template <typename U> inline constexpr auto operator %(U n) const noexcept { return get() % n; }

    template <typename U> inline constexpr auto operator &(U n) const noexcept { return get() & n; }
    template <typename U> inline constexpr auto operator |(U n) const noexcept { return get() | n; }
    template <typename U> inline constexpr auto operator ^(U n) const noexcept { return get() ^ n; }

    inline constexpr T operator <<(int n) const noexcept { return get() << n; }
    inline constexpr T operator >>(int n) const noexcept { return get() >> n; }

    inline constexpr auto operator =(T n) { return set(n); }
    constexpr byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;

    template <typename U> inline constexpr auto operator +=(U n) noexcept { return set(get() + n); }
    template <typename U> inline constexpr auto operator -=(U n) noexcept { return set(get() - n); }
    template <typename U> inline constexpr auto operator *=(U n) noexcept { return set(get() * n); }
    template <typename U> inline constexpr auto operator /=(U n) noexcept { return set(get() / n); }
    template <typename U> inline constexpr auto operator %=(U n) noexcept { return set(get() % n); }

    inline constexpr auto operator &=(T n) noexcept { return set(get() & n); }
    inline constexpr auto operator |=(T n) noexcept { return set(get() | n); }
    inline constexpr auto operator ^=(T n) noexcept { return set(get() ^ n); }

    inline constexpr auto operator <<=(int n) noexcept { return set(get() << n); }
    inline constexpr auto operator >>=(int n) noexcept { return set(get() >> n); }

protected:
    inline constexpr T get() const noexcept { return native_cast(_v); }
    inline constexpr auto set(T n) noexcept { _v = bswap_cast(n); return *this; }
};
```


### C++11 からの変更

#### 返り値の型に auto を使用する

```diff:更新差分(抜粋)
-    template <typename U> inline constexpr decltype(T() + U()) operator +(U n) const noexcept { return get() + n; }
+    template <typename U> inline constexpr auto operator +(U n) const noexcept { return get() + n; }
```

<code>return</code> 文から型推定してくれるハズ。

#### const でないメンバ関数も constexpr とする

```diff:更新差分(抜粋)
-    template <typename U> inline byteswap_value<T>& operator +=(U n) noexcept { return set(get() + n); }
+    template <typename U> inline constexpr auto operator +=(U n) noexcept { return set(get() + n); }
```

アプリの constexpr 関数内のローカル変数に byteswap_value&lt;T&gt; 型が使用できるハズ。

#### その他

- 型選択を std::conditional から std::conditional_t に変更

### テスト プログラム

<details><summary>endian-14.cpp (やや長いので折りたたみ)</summary><div>

```C++:endian-14.cpp
#include "endian.hpp"
#include "byteswap.hpp"

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

#include <cstdint>
#include <limits>

/*
 * byteswap_value
 */
template <typename T>
class byteswap_value
{
public:
    using value_type = T;  // byteswap する native の型.

protected:

    /* 逆バイト順型 */
    template <typename I> struct bswap { using type = I; };
    template <> struct bswap<float> { using type = std::uint32_t; };
    template <> struct bswap<double> { using type = std::uint64_t; };

    using bswap_value = typename bswap<T>::type;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline constexpr bswap_cast(const T& n) noexcept : v(n) {}
        inline constexpr operator bswap_value() const noexcept
            { return std::byteswap(*(const bswap_value*)&v); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline constexpr native_cast(const bswap_value& s) noexcept : v(std::byteswap(s)) {}
        inline constexpr operator T() const noexcept { return *(const T*)&v; }
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    byteswap_value() noexcept = default;
    inline constexpr byteswap_value(T d) noexcept : _v(bswap_cast(d)) {}
    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;

    inline constexpr operator T() const noexcept { return get(); }

    inline constexpr bool operator !() const noexcept { return !_v; }
    inline constexpr bool operator ==(const byteswap_value<T>& s) const noexcept { return _v == s._v; }
    inline constexpr bool operator !=(const byteswap_value<T>& s) const noexcept { return _v != s._v; }

    template <typename U> inline constexpr bool operator < (U n) const noexcept { return get() <  n; }
    template <typename U> inline constexpr bool operator <=(U n) const noexcept { return get() <= n; }
    template <typename U> inline constexpr bool operator ==(U n) const noexcept { return get() == n; }
    template <typename U> inline constexpr bool operator !=(U n) const noexcept { return get() != n; }
    template <typename U> inline constexpr bool operator >=(U n) const noexcept { return get() >= n; }
    template <typename U> inline constexpr bool operator > (U n) const noexcept { return get() >  n; }

    inline constexpr T operator +() const noexcept { return +get(); }
    inline constexpr T operator -() const noexcept { return -get(); }
    inline constexpr T operator ~() const noexcept { return ~get(); }

    template <typename U> inline constexpr auto operator +(U n) const noexcept { return get() + n; }
    template <typename U> inline constexpr auto operator -(U n) const noexcept { return get() - n; }
    template <typename U> inline constexpr auto operator *(U n) const noexcept { return get() * n; }
    template <typename U> inline constexpr auto operator /(U n) const noexcept { return get() / n; }
    template <typename U> inline constexpr auto operator %(U n) const noexcept { return get() % n; }

    template <typename U> inline constexpr auto operator &(U n) const noexcept { return get() & n; }
    template <typename U> inline constexpr auto operator |(U n) const noexcept { return get() | n; }
    template <typename U> inline constexpr auto operator ^(U n) const noexcept { return get() ^ n; }

    inline constexpr T operator <<(int n) const noexcept { return get() << n; }
    inline constexpr T operator >>(int n) const noexcept { return get() >> n; }

    inline constexpr auto operator =(T n) { return set(n); }
    constexpr byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;

    template <typename U> inline constexpr auto operator +=(U n) noexcept { return set(get() + n); }
    template <typename U> inline constexpr auto operator -=(U n) noexcept { return set(get() - n); }
    template <typename U> inline constexpr auto operator *=(U n) noexcept { return set(get() * n); }
    template <typename U> inline constexpr auto operator /=(U n) noexcept { return set(get() / n); }
    template <typename U> inline constexpr auto operator %=(U n) noexcept { return set(get() % n); }

    inline constexpr auto operator &=(T n) noexcept { return set(get() & n); }
    inline constexpr auto operator |=(T n) noexcept { return set(get() | n); }
    inline constexpr auto operator ^=(T n) noexcept { return set(get() ^ n); }

    inline constexpr auto operator <<=(int n) noexcept { return set(get() << n); }
    inline constexpr auto operator >>=(int n) noexcept { return set(get() >> n); }

protected:
    inline constexpr T get() const noexcept { return native_cast(_v); }
    inline constexpr auto set(T n) noexcept { _v = bswap_cast(n); return *this; }
};

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

#include <string>

struct native_endian
{
    enum {
        is_big = std::endian::big == std::endian::native,
        is_little = std::endian::little == std::endian::native,
    };

    using int8_type = std::int8_t;
    using int16_type = std::int16_t;
    using int32_type = std::int32_t;
    using int64_type = std::int64_t;

    using uint8_type = std::uint8_t;
    using uint16_type = std::uint16_t;
    using uint32_type = std::uint32_t;
    using uint64_type = std::uint64_t;

    using float32_type = float;
    using float64_type = double;

    using char_type = char;
    using wchar_type = wchar_t;
    using char16_type = char16_t;
    using char32_type = char32_t;
};

struct byteswap_endian
{
    enum {
        is_big = !native_endian::is_big,
        is_little = !native_endian::is_little,
    };

    using int8_type = native_endian::int8_type;
    using int16_type = byteswap_value<native_endian::int16_type>;
    using int32_type = byteswap_value<native_endian::int32_type>;
    using int64_type = byteswap_value<native_endian::int64_type>;

    using uint8_type = native_endian::uint8_type;
    using uint16_type = byteswap_value<native_endian::uint16_type>;
    using uint32_type = byteswap_value<native_endian::uint32_type>;
    using uint64_type = byteswap_value<native_endian::uint64_type>;

    using float32_type = byteswap_value<native_endian::float32_type>;
    using float64_type = byteswap_value<native_endian::float64_type>;

    using char_type = byteswap_value<native_endian::char_type>;
    using wchar_type = byteswap_value<native_endian::wchar_type>;
    using char16_type = byteswap_value<native_endian::char16_type>;
    using char32_type = byteswap_value<native_endian::char32_type>;
};

template <bool big>
using fixed_endian = std::conditional_t<
    big == native_endian::is_big, native_endian, byteswap_endian>;

/* big endian */

using big_endian = fixed_endian<true>;

using int8be_t = big_endian::int8_type;
using int16be_t = big_endian::int16_type;
using int32be_t = big_endian::int32_type;
using int64be_t = big_endian::int64_type;

using uint8be_t = big_endian::uint8_type;
using uint16be_t = big_endian::uint16_type;
using uint32be_t = big_endian::uint32_type;
using uint64be_t = big_endian::uint64_type;

using float32be_t = big_endian::float32_type;
using float64be_t = big_endian::float64_type;

using charbe_t = big_endian::char_type;
using wcharbe_t = big_endian::wchar_type;
using char16be_t = big_endian::char16_type;
using char32be_t = big_endian::char32_type;

using stringbe = std::basic_string<charbe_t>;
using wstringbe = std::basic_string<wcharbe_t>;
using u16stringbe = std::basic_string<char16be_t>;
using u32stringbe = std::basic_string<char32be_t>;


/* little endian */

using little_endian = fixed_endian<false>;

using int8le_t = little_endian::int8_type;
using int16le_t = little_endian::int16_type;
using int32le_t = little_endian::int32_type;
using int64le_t = little_endian::int64_type;

using uint8le_t = little_endian::uint8_type;
using uint16le_t = little_endian::uint16_type;
using uint32le_t = little_endian::uint32_type;
using uint64le_t = little_endian::uint64_type;

using float32le_t = little_endian::float32_type;
using float64le_t = little_endian::float64_type;

using charle_t = little_endian::char_type;
using wcharle_t = little_endian::wchar_type;
using char16le_t = little_endian::char16_type;
using char32le_t = little_endian::char32_type;

using stringle = std::basic_string<charle_t>;
using wstringle = std::basic_string<wcharle_t>;
using u16stringle = std::basic_string<char16le_t>;
using u32stringle = std::basic_string<char32le_t>;


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

/*
 * テスト
 */

#include <algorithm>
#include <cstdio>
#include <typeinfo>

static const uint16_t native_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static const uint16be_t big_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static const uint16le_t little_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};

static const uint32_t native_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static const uint32be_t big_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static const uint32le_t little_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};

static const uint64_t native_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static const uint64be_t big_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static const uint64le_t little_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};

static const float native_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32be_t big_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32le_t little_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};

static const double native_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64be_t big_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64le_t little_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};

static void dumpbin(const char* msg, const void* s, size_t n)
{
    const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
    size_t x, y, ny = n & ~15, nr = n & 15;

    printf("address = %p: %s\n", s, msg);
    for (y = 0; y < ny; y += 16)
    {
        printf("   ");
        for (size_t x = 0; x < 16; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
    if (y < n)
    {
        printf("   ");
        for (x = 0; x < nr; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
}

template <typename T>
inline static void dumpbin(const char* msg, const T& s)
{
    dumpbin(msg, &s, sizeof(T));
}

template <typename T>
inline static void dumpbin(const char* msg, const std::basic_string<T>& s)
{
    dumpbin(msg, s.data(), s.size() * sizeof(T));
}

template <typename T>
inline static const char *gettypename()
{
    return typeid(T()).name();
}

template <typename T>
inline static const char *gettypename(const T v)
{
    return typeid(v).name();
}

template <typename T>
inline static const char *valuetypename()
{
    return typeid(T(0)).name();
}

template <typename T>
static void rtypename(const char* name, const T& x)
{

    int8_t i8 = 11;
    int16_t i16 = 12;
    int32_t i32 = 14;
    int64_t i64 = 18;
    uint8_t u8 = 21;
    uint16_t u16 = 22;
    uint32_t u32 = 24;
    uint64_t u64 = 28;
    float f32 = 1.5;
    double f64 = 2.25;

    char c8 = 1;
    wchar_t wc = 2;
    char16_t c16 = 2;
    char32_t c32 = 4;

    printf("%s = %s\n", name, gettypename(x));
    printf("    +i8:%s  +i16:%s  +i32:%s  +i64:%s\n",
           gettypename(x + i8), gettypename(x + i16),
           gettypename(x + i32), gettypename(x + i64));
    printf("    +u8:%s  +u16:%s  +u32:%s  +u64:%s\n",
           gettypename(x + u8), gettypename(x + u16),
           gettypename(x + u32), gettypename(x + u64));
    printf("    +c8:%s  +c16:%s  +c32:%s  +wc:%s\n",
           gettypename(x + c8), gettypename(x + c16),
           gettypename(x + c32), gettypename(x + wc));
    printf("    +f32:%s  +f64:%s\n",
           gettypename(x + f32), gettypename(x + f64));
    printf("\n");

#if 0
    T y = 0;

    printf("    +=i8:%s  +=i16:%s  +=i32:%s  +i64:%s\n",
           gettypename(y += i8), gettypename(y += i16),
           gettypename(y += i32), gettypename(y += i64));
    printf("    +=u8:%s  +=u16:%s  +=u32:%s  +=u64:%s\n",
           gettypename(y += u8), gettypename(y += u16),
           gettypename(y += u32), gettypename(y += u64));
    printf("    +=f32:%s  +=f64:%s\n",
           gettypename(y += f32), gettypename(y += f64));
    printf("    +=c8:%s  +=wc:%s\n",
           gettypename(y += c8), gettypename(y += wc));
    printf("\n");
#endif
}

/*
 *
 */

int main()
{
#ifdef __cplusplus
    printf("compiler: __cplusplus = %ld\n", __cplusplus);
#endif
#ifdef _MSVC_LANG
    printf("compiler: _MSVC_LANG = %ld\n", _MSVC_LANG);
#endif

    printf("endian: native = %s\n", native_endian::is_big ? "big" : "little");
    printf("\n");

    dumpbin("native_data_u16", native_data_u16);
    dumpbin("big_data_u16", big_data_u16);
    dumpbin("little_data_u16", little_data_u16);
    printf("\n");

    dumpbin("native_data_u32", native_data_u32);
    dumpbin("big_data_u32", big_data_u32);
    dumpbin("little_data_u32", little_data_u32);
    printf("\n");

    dumpbin("native_data_u64", native_data_u64);
    dumpbin("big_data_u64", big_data_u64);
    dumpbin("little_data_u64", little_data_u64);
    printf("\n");

    dumpbin("native_data_f32", native_data_f32);
    dumpbin("big_data_f32", big_data_f32);
    dumpbin("little_data_f32", little_data_f32);
    printf("\n");

    dumpbin("native_data_f64", native_data_f64);
    dumpbin("big_data_f64", big_data_f64);
    dumpbin("little_data_f64", little_data_f64);
    printf("\n");

    int16be_t i16be = 32;
    int32be_t i32be = 34;
    int64be_t i64be = 38;
    uint16be_t u16be = 42;
    uint32be_t u32be = 44;
    uint64be_t u64be = 48;
    float32be_t f32be = 4.25;
    float64be_t f64be = 8.125;

    int16le_t i16le = i16be;
    int32le_t i32le = i32be;
    int64le_t i64le = i64be;
    uint16le_t u16le = u16be;
    uint32le_t u32le = u32be;
    uint64le_t u64le = u64be;
    float32le_t f32le = f32be;
    float64le_t f64le = f64be;

    /**/

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    dumpbin("float32be_t", f32be); dumpbin("float32le_t", f32le);
    dumpbin("float64be_t", f64be); dumpbin("float64le_t", f64le);
    printf("\n");

    /**/

    u16be = 32; u32be = 34; u64be = 38;
    i16be = 42; i32be = 44; i64be = 48;
    u16le = 32; u32le = 34; u64le = 38;
    i16le = 42; i32le = 44; i64le = 48;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    u16be <<= 4; u32be <<= 4; u64be <<= 4;
    i16be <<= 4; i32be <<= 4; i64be <<= 4;
    u16le <<= 4; u32le <<= 4; u64le <<= 4;
    i16le <<= 4; i32le <<= 4; i64le <<= 4;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    printf("compare:\n");
#define COMP(l,op,r)                            \
    printf("    %s(%d) %-2s %s(%d) : %s\n",     \
           #l, (int)l, #op, #r, (int)r,         \
           (l op r) ? "true" : "false")
    COMP(i16be, < , i16le); COMP(i16be, <=, i16le); COMP(i16be, ==, i16le);
    COMP(i16be, !=, i16le); COMP(i16be, >=, i16le); COMP(i16be, > , i16le);
    printf("\n");
    COMP(i32be, < , i16le); COMP(i32be, <=, i16le); COMP(i32be, ==, i16le);
    COMP(i32be, !=, i16le); COMP(i32be, >=, i16le); COMP(i32be, > , i16le);
    printf("\n");
    COMP(i32be, < , i64be); COMP(i32be, <=, i64be); COMP(i32be, ==, i64be);
    COMP(i32be, !=, i64be); COMP(i32be, >=, i64be); COMP(i32be, > , i64be);
    printf("\n");
    COMP(i32be, < , i64le); COMP(i32be, <=, i64le); COMP(i32be, ==, i64le);
    COMP(i32be, !=, i64le); COMP(i32be, >=, i64le); COMP(i32be, > , i64le);
    printf("\n");

    /**/

    printf("typeid:\n");
    printf("    int8_t:%s  int16_t:%s  int32_t:%s  int64_t:%s\n",
           valuetypename<int8_t>(), valuetypename<int16_t>(),
           valuetypename<int32_t>(), valuetypename<int64_t>());
    printf("    uint8_t:%s  uint16_t:%s  uint32_t:%s  uint64_t:%s\n",
           valuetypename<uint8_t>(), valuetypename<uint16_t>(),
           valuetypename<uint32_t>(), valuetypename<uint64_t>());
    printf("    float:%s  double:%s\n", valuetypename<float>(), valuetypename<double>());
    printf("    char:%s  wchar_t:%s\n", valuetypename<char>(), valuetypename<wchar_t>());
    printf("    int:%s  unsigned:%s\n", valuetypename<int>(), valuetypename<unsigned>());
    printf("    sizeof(wchar_t):%zd\n", sizeof(wchar_t));
    printf("\n");

    /**/

    rtypename("int16be_t", i16be); rtypename("int16le_t", i16le);
    rtypename("int32be_t", i32be); rtypename("int32le_t", i32le);
    rtypename("int64be_t", i64be); rtypename("int64le_t", i64le);

    rtypename("uint16be_t", u16be); rtypename("uint16le_t", u16le);
    rtypename("uint32be_t", u32be); rtypename("uint32le_t", u32le);
    rtypename("uint64be_t", u64be); rtypename("uint64le_t", u64le);

    rtypename("float32be_t", f32be); rtypename("float32le_t", f32le);
    rtypename("float64be_t", f64be); rtypename("float64le_t", f64le);

    /**/

    std::wstring ws(L"サンプルTEXT");
    wstringbe wsbe(ws.begin(), ws.end());
    wstringle wsle(ws.begin(), ws.end());
    dumpbin("wstring", ws);
    dumpbin("wstringbe", wsbe);
    dumpbin("wstringle", wsle);
    printf("\n");

    std::u16string u16s(ws.begin(), ws.end()); /* sizeof(wchar_t) != sizeof(char16_t) の場合は要注意 */
    u16stringbe u16sbe(u16s.begin(), u16s.end());
    u16stringle u16sle(u16s.begin(), u16s.end());
    dumpbin("u16string", u16s);
    dumpbin("u16stringbe", u16sbe);
    dumpbin("u16stringle", u16sle);
    printf("\n");

    std::u32string u32s(ws.begin(), ws.end()); /* sizeof(wchar_t) != sizeof(char32_t) の場合は要注意 */
    u32stringbe u32sbe(u32s.begin(), u32s.end());
    u32stringle u32sle(u32s.begin(), u32s.end());
    dumpbin("u32string", u32s);
    dumpbin("u32stringbe", u32sbe);
    dumpbin("u32stringle", u32sle);
    printf("\n");

    return 0;
}
```

```console:実行結果
$ clang++ -O3 -g0 -DNDEBUG=1 -std=c++14 endian-14.cpp -o endian-14
$ ./endian-14
compiler: __cplusplus = 201402
endian: native = little

address = 0x10370b900: native_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32
address = 0x10370b910: big_data_u16
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10370b920: little_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32

address = 0x10370b930: native_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76
address = 0x10370b940: big_data_u32
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10370b950: little_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76

address = 0x10370b960: native_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe
address = 0x10370b970: big_data_u64
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x10370b980: little_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe

address = 0x10370b990: native_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76
address = 0x10370d000: big_data_f32
    34 56 78 9a 45 67 89 ab 56 78 9a bc 76 54 32 10
address = 0x10370d030: little_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76

address = 0x10370b9a0: native_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76
address = 0x10370d010: big_data_f64
    34 56 78 9a bc de f0 12 45 67 89 ab cd ef 01 23
    56 78 9a bc de f0 12 34 76 54 32 10 fe dc ba 98
address = 0x10370d040: little_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76

address = 0x7ff7bc7f8630: i16be_t
    00 20
address = 0x7ff7bc7f8636: i16le_t
    20 00
address = 0x7ff7bc7f8620: u16be_t
    00 2a
address = 0x7ff7bc7f861e: u16le_t
    2a 00
address = 0x7ff7bc7f8628: i32be_t
    00 00 00 22
address = 0x7ff7bc7f8600: i32le_t
    22 00 00 00
address = 0x7ff7bc7f8618: u32be_t
    00 00 00 2c
address = 0x7ff7bc7f8604: u32le_t
    2c 00 00 00
address = 0x7ff7bc7f8610: i64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7bc7f8608: i64le_t
    26 00 00 00 00 00 00 00
address = 0x7ff7bc7f85f8: u64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7bc7f85f0: u64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7bc7f85e8: float32be_t
    40 88 00 00
address = 0x7ff7bc7f85e4: float32le_t
    00 00 88 40
address = 0x7ff7bc7f85d0: float64be_t
    40 20 40 00 00 00 00 00
address = 0x7ff7bc7f85d8: float64le_t
    00 00 00 00 00 40 20 40

address = 0x7ff7bc7f8630: i16be_t
    00 2a
address = 0x7ff7bc7f8636: i16le_t
    2a 00
address = 0x7ff7bc7f8620: u16be_t
    00 20
address = 0x7ff7bc7f861e: u16le_t
    20 00
address = 0x7ff7bc7f8628: i32be_t
    00 00 00 2c
address = 0x7ff7bc7f8600: i32le_t
    2c 00 00 00
address = 0x7ff7bc7f8618: u32be_t
    00 00 00 22
address = 0x7ff7bc7f8604: u32le_t
    22 00 00 00
address = 0x7ff7bc7f8610: i64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7bc7f8608: i64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7bc7f85f8: u64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7bc7f85f0: u64le_t
    26 00 00 00 00 00 00 00

address = 0x7ff7bc7f8630: i16be_t
    02 a0
address = 0x7ff7bc7f8636: i16le_t
    a0 02
address = 0x7ff7bc7f8620: u16be_t
    02 00
address = 0x7ff7bc7f861e: u16le_t
    00 02
address = 0x7ff7bc7f8628: i32be_t
    00 00 02 c0
address = 0x7ff7bc7f8600: i32le_t
    c0 02 00 00
address = 0x7ff7bc7f8618: u32be_t
    00 00 02 20
address = 0x7ff7bc7f8604: u32le_t
    20 02 00 00
address = 0x7ff7bc7f8610: i64be_t
    00 00 00 00 00 00 03 00
address = 0x7ff7bc7f8608: i64le_t
    00 03 00 00 00 00 00 00
address = 0x7ff7bc7f85f8: u64be_t
    00 00 00 00 00 00 02 60
address = 0x7ff7bc7f85f0: u64le_t
    60 02 00 00 00 00 00 00

compare:
    i16be(672) <  i16le(672) : false
    i16be(672) <= i16le(672) : true
    i16be(672) == i16le(672) : true
    i16be(672) != i16le(672) : false
    i16be(672) >= i16le(672) : true
    i16be(672) >  i16le(672) : false

    i32be(704) <  i16le(672) : false
    i32be(704) <= i16le(672) : false
    i32be(704) == i16le(672) : false
    i32be(704) != i16le(672) : true
    i32be(704) >= i16le(672) : true
    i32be(704) >  i16le(672) : true

    i32be(704) <  i64be(768) : true
    i32be(704) <= i64be(768) : true
    i32be(704) == i64be(768) : false
    i32be(704) != i64be(768) : true
    i32be(704) >= i64be(768) : false
    i32be(704) >  i64be(768) : false

    i32be(704) <  i64le(768) : true
    i32be(704) <= i64le(768) : true
    i32be(704) == i64le(768) : false
    i32be(704) != i64le(768) : true
    i32be(704) >= i64le(768) : false
    i32be(704) >  i64le(768) : false

typeid:
    int8_t:a  int16_t:s  int32_t:i  int64_t:x
    uint8_t:h  uint16_t:t  uint32_t:j  uint64_t:y
    float:f  double:d
    char:c  wchar_t:w
    int:i  unsigned:j
    sizeof(wchar_t):4

int16be_t = 14byteswap_valueIsE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int16le_t = s
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int32be_t = 14byteswap_valueIiE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int32le_t = i
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int64be_t = 14byteswap_valueIxE
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +c8:x  +c16:x  +c32:x  +wc:x
    +f32:f  +f64:d

int64le_t = x
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +c8:x  +c16:x  +c32:x  +wc:x
    +f32:f  +f64:d

uint16be_t = 14byteswap_valueItE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

uint16le_t = t
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

uint32be_t = 14byteswap_valueIjE
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +c8:j  +c16:j  +c32:j  +wc:j
    +f32:f  +f64:d

uint32le_t = j
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +c8:j  +c16:j  +c32:j  +wc:j
    +f32:f  +f64:d

uint64be_t = 14byteswap_valueIyE
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +c8:y  +c16:y  +c32:y  +wc:y
    +f32:f  +f64:d

uint64le_t = y
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +c8:y  +c16:y  +c32:y  +wc:y
    +f32:f  +f64:d

float32be_t = 14byteswap_valueIfE
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +c8:f  +c16:f  +c32:f  +wc:f
    +f32:f  +f64:d

float32le_t = f
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +c8:f  +c16:f  +c32:f  +wc:f
    +f32:f  +f64:d

float64be_t = 14byteswap_valueIdE
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +c8:d  +c16:d  +c32:d  +wc:d
    +f32:d  +f64:d

float64le_t = d
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +c8:d  +c16:d  +c32:d  +wc:d
    +f32:d  +f64:d

address = 0x6000021b4000: wstring
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
address = 0x6000021b4030: wstringbe
    00 00 30 b5 00 00 30 f3 00 00 30 d7 00 00 30 eb
    00 00 00 54 00 00 00 45 00 00 00 58 00 00 00 54
address = 0x6000021b4060: wstringle
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00

address = 0x7ff7bc7f859a: u16string
    b5 30 f3 30 d7 30 eb 30 54 00 45 00 58 00 54 00
address = 0x7ff7bc7f8552: u16stringbe
    30 b5 30 f3 30 d7 30 eb 00 54 00 45 00 58 00 54
address = 0x7ff7bc7f856a: u16stringle
    b5 30 f3 30 d7 30 eb 30 54 00 45 00 58 00 54 00

address = 0x6000021b4090: u32string
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
address = 0x6000021b40c0: u32stringbe
    00 00 30 b5 00 00 30 f3 00 00 30 d7 00 00 30 eb
    00 00 00 54 00 00 00 45 00 00 00 58 00 00 00 54
address = 0x6000021b40f0: u32stringle
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
```

</div></details>

## C++17 では byteswap_value に変化なし

<code>std::basic_string_view</code> を使う場合は

```diff:C++14からの更新差分(抜粋)
+using wstringbe_view = std::basic_string_view<wcharbe_t>;
+using u16stringbe_view = std::basic_string_view<char16be_t>;
+using u32stringbe_view = std::basic_string_view<char32be_t>;

+using wstringle_view = std::basic_string_view<wcharle_t>;
+using u16stringle_view = std::basic_string_view<char16le_t>;
+using u32stringle_view = std::basic_string_view<char32le_t>;
```

が可能。


## C++20 で書いてみる

```C++:byteswap値型テンプレート&nbsp;クラス
template <typename T>
class byteswap_value
{
public:
    using value_type = T;  // byteswap する native の型.

protected:

    /* 逆バイト順型 */
    template <typename I> struct bswap { using type = I; };
    template <> struct bswap<float> { using type = std::uint32_t; };
    template <> struct bswap<double> { using type = std::uint64_t; };

    using bswap_value = bswap<T>::type;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline constexpr bswap_cast(const T& n) noexcept : v(n) {}
        inline constexpr operator bswap_value() const noexcept
            { return std::byteswap(std::bit_cast<bswap_value>(v)); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline constexpr native_cast(const bswap_value& s) noexcept : v(std::byteswap(s)) {}
        inline constexpr operator T() const noexcept { return std::bit_cast<T>(v); }
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    byteswap_value() noexcept = default;
    inline constexpr byteswap_value(T d) noexcept : _v(bswap_cast(d)) {}
    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;

    inline constexpr operator T() const noexcept { return get(); }

    inline constexpr bool operator !() const noexcept { return !_v; }
    inline constexpr bool operator ==(const byteswap_value<T>& s) const noexcept { return _v == s._v; }
    inline constexpr bool operator ==(auto n) const noexcept { return get() == n; }
    inline constexpr auto operator <=>(auto n) const noexcept { return get() <=> n; }

    inline constexpr T operator +() const noexcept { return +get(); }
    inline constexpr T operator -() const noexcept { return -get(); }
    inline constexpr T operator ~() const noexcept { return ~get(); }

    inline constexpr auto operator +(auto n) const noexcept { return get() + n; }
    inline constexpr auto operator -(auto n) const noexcept { return get() - n; }
    inline constexpr auto operator *(auto n) const noexcept { return get() * n; }
    inline constexpr auto operator /(auto n) const noexcept { return get() / n; }
    inline constexpr auto operator %(auto n) const noexcept { return get() % n; }

    inline constexpr auto operator &(auto n) const noexcept { return get() & n; }
    inline constexpr auto operator |(auto n) const noexcept { return get() | n; }
    inline constexpr auto operator ^(auto n) const noexcept { return get() ^ n; }

    inline constexpr T operator <<(int n) const noexcept { return get() << n; }
    inline constexpr T operator >>(int n) const noexcept { return get() >> n; }

    inline constexpr auto operator =(T n) { return set(n); }
    constexpr byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;

    inline constexpr auto operator +=(auto n) noexcept { return set(get() + n); }
    inline constexpr auto operator -=(auto n) noexcept { return set(get() - n); }
    inline constexpr auto operator *=(auto n) noexcept { return set(get() * n); }
    inline constexpr auto operator /=(auto n) noexcept { return set(get() / n); }
    inline constexpr auto operator %=(auto n) noexcept { return set(get() % n); }

    inline constexpr auto operator &=(T n) noexcept { return set(get() & n); }
    inline constexpr auto operator |=(T n) noexcept { return set(get() | n); }
    inline constexpr auto operator ^=(T n) noexcept { return set(get() ^ n); }

    inline constexpr auto operator <<=(int n) noexcept { return set(get() << n); }
    inline constexpr auto operator >>=(int n) noexcept { return set(get() >> n); }

protected:
    inline constexpr T get() const noexcept { return native_cast(_v); }
    inline constexpr auto set(T n) noexcept { _v = bswap_cast(n); return *this; }
};
```

### C++14 からの変更

#### typename の省略

typename キーワードが不要な場合がある。

```diff:更新差分(抜粋)
-    using bswap_value = typename bswap<T>::type;  // byteswap 値の型.
+    using bswap_value = bswap<T>::type;  // byteswap 値の型.
```

#### メンバ関数の引数に auto を使用する

テンプレート部分が不要になった。

```diff:更新差分(抜粋)
-    template <typename U> inline constexpr auto operator +(U n) const noexcept { return get() + n; }
+    inline constexpr auto operator +(auto n) const noexcept { return get() + n; }
```

#### 関係演算子の省略

三方比較演算子 <code>'&lt;=&gt;'</code> により <code>'&lt;', '&lt;=', '&gt;=', '&gt;'</code> を、等値演算子 <code>'=='</code> により、非等値演算子 <code>'!='</code> を省略した。

```diff:更新差分(抜粋)
+    inline constexpr bool operator ==(auto n) const noexcept { return get() == n; }
+    inline constexpr auto operator <=>(auto n) const noexcept { return get() <=> n; }
-    inline constexpr bool operator !=(const byteswap_value<T>& s) const noexcept { return _v != s._v; }
-    template <typename U> inline constexpr bool operator < (U n) const noexcept { return get() <  n; }
-    template <typename U> inline constexpr bool operator <=(U n) const noexcept { return get() <= n; }
-    template <typename U> inline constexpr bool operator ==(U n) const noexcept { return get() == n; }
-    template <typename U> inline constexpr bool operator !=(U n) const noexcept { return get() != n; }
-    template <typename U> inline constexpr bool operator >=(U n) const noexcept { return get() >= n; }
-    template <typename U> inline constexpr bool operator > (U n) const noexcept { return get() >  n; }
```


#### std::bit_cast を使用する

@yaito3014 さんの指摘により追加

```diff:更新差分(抜粋)
-            { return std::byteswap(*(const bswap_value*)&v); }
+            { return std::byteswap(std::bit_cast<bswap_value>(v)); }
```

```diff:更新差分(抜粋)
-        inline constexpr operator T() const noexcept { return *(const T*)&v; }
+        inline constexpr operator T() const noexcept { return std::bit_cast<T>(v); }
```

#### その他

- <code>char8_t</code> に対する定義を追加(各エンディアン型)

### テスト プログラム

<details><summary>endian-20.cpp (やや長いので折りたたみ)</summary><div>

```C++:endian-20.cpp
#include "endian.hpp"
#include "byteswap.hpp"

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

#include <cstdint>
#include <compare>
#include <limits>

/*
 * byteswap_value
 */
template <typename T>
class byteswap_value
{
public:
    using value_type = T;  // byteswap する native の型.

protected:

    /* 逆バイト順型 */
    template <typename I> struct bswap { using type = I; };
    template <> struct bswap<float> { using type = std::uint32_t; };
    template <> struct bswap<double> { using type = std::uint64_t; };

    using bswap_value = bswap<T>::type;  // byteswap 値の型.

    /* bswap_value への型変換 */
    struct bswap_cast
    {
        T v;

        inline constexpr bswap_cast(const T& n) noexcept : v(n) {}
        inline constexpr operator bswap_value() const noexcept
            { return std::byteswap(std::bit_cast<bswap_value>(v)); }
    };

    /* bswap_value からの型変換 */
    struct native_cast
    {
        bswap_value v;

        inline constexpr native_cast(const bswap_value& s) noexcept : v(std::byteswap(s)) {}
        inline constexpr operator T() const noexcept { return std::bit_cast<T>(v); }
    };

protected:

    bswap_value _v;  // byteswap された値を保持する.

public:
    byteswap_value() noexcept = default;
    inline constexpr byteswap_value(T d) noexcept : _v(bswap_cast(d)) {}
    constexpr byteswap_value(const byteswap_value<T>& s) noexcept = default;

    inline constexpr operator T() const noexcept { return get(); }

    inline constexpr bool operator !() const noexcept { return !_v; }
    inline constexpr bool operator ==(const byteswap_value<T>& s) const noexcept { return _v == s._v; }
    inline constexpr bool operator ==(auto n) const noexcept { return get() == n; }
    inline constexpr auto operator <=>(auto n) const noexcept { return get() <=> n; }

    inline constexpr T operator +() const noexcept { return +get(); }
    inline constexpr T operator -() const noexcept { return -get(); }
    inline constexpr T operator ~() const noexcept { return ~get(); }

    inline constexpr auto operator +(auto n) const noexcept { return get() + n; }
    inline constexpr auto operator -(auto n) const noexcept { return get() - n; }
    inline constexpr auto operator *(auto n) const noexcept { return get() * n; }
    inline constexpr auto operator /(auto n) const noexcept { return get() / n; }
    inline constexpr auto operator %(auto n) const noexcept { return get() % n; }

    inline constexpr auto operator &(auto n) const noexcept { return get() & n; }
    inline constexpr auto operator |(auto n) const noexcept { return get() | n; }
    inline constexpr auto operator ^(auto n) const noexcept { return get() ^ n; }

    inline constexpr T operator <<(int n) const noexcept { return get() << n; }
    inline constexpr T operator >>(int n) const noexcept { return get() >> n; }

    inline constexpr auto operator =(T n) { return set(n); }
    constexpr byteswap_value<T>& operator =(const byteswap_value<T>& s) noexcept = default;

    inline constexpr auto operator +=(auto n) noexcept { return set(get() + n); }
    inline constexpr auto operator -=(auto n) noexcept { return set(get() - n); }
    inline constexpr auto operator *=(auto n) noexcept { return set(get() * n); }
    inline constexpr auto operator /=(auto n) noexcept { return set(get() / n); }
    inline constexpr auto operator %=(auto n) noexcept { return set(get() % n); }

    inline constexpr auto operator &=(T n) noexcept { return set(get() & n); }
    inline constexpr auto operator |=(T n) noexcept { return set(get() | n); }
    inline constexpr auto operator ^=(T n) noexcept { return set(get() ^ n); }

    inline constexpr auto operator <<=(int n) noexcept { return set(get() << n); }
    inline constexpr auto operator >>=(int n) noexcept { return set(get() >> n); }

protected:
    inline constexpr T get() const noexcept { return native_cast(_v); }
    inline constexpr auto set(T n) noexcept { _v = bswap_cast(n); return *this; }
};

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

#include <string>
#include <string_view>

struct native_endian
{
    enum {
        is_big = std::endian::big == std::endian::native,
        is_little = std::endian::little == std::endian::native,
    };

    using int8_type = std::int8_t;
    using int16_type = std::int16_t;
    using int32_type = std::int32_t;
    using int64_type = std::int64_t;

    using uint8_type = std::uint8_t;
    using uint16_type = std::uint16_t;
    using uint32_type = std::uint32_t;
    using uint64_type = std::uint64_t;

    using float32_type = float;
    using float64_type = double;

    using char_type = char;
    using wchar_type = wchar_t;
    using char8_type = char8_t;
    using char16_type = char16_t;
    using char32_type = char32_t;
};

struct byteswap_endian
{
    enum {
        is_big = !native_endian::is_big,
        is_little = !native_endian::is_little,
    };

    using int8_type = native_endian::int8_type;
    using int16_type = byteswap_value<native_endian::int16_type>;
    using int32_type = byteswap_value<native_endian::int32_type>;
    using int64_type = byteswap_value<native_endian::int64_type>;

    using uint8_type = native_endian::uint8_type;
    using uint16_type = byteswap_value<native_endian::uint16_type>;
    using uint32_type = byteswap_value<native_endian::uint32_type>;
    using uint64_type = byteswap_value<native_endian::uint64_type>;

    using float32_type = byteswap_value<native_endian::float32_type>;
    using float64_type = byteswap_value<native_endian::float64_type>;

    using char_type = byteswap_value<native_endian::char_type>;
    using wchar_type = byteswap_value<native_endian::wchar_type>;
    using char8_type = byteswap_value<native_endian::char8_type>;
    using char16_type = byteswap_value<native_endian::char16_type>;
    using char32_type = byteswap_value<native_endian::char32_type>;
};

template <bool big>
using fixed_endian = std::conditional_t<
    big == native_endian::is_big, native_endian, byteswap_endian>;

/* big endian */

using big_endian = fixed_endian<true>;

using int8be_t = big_endian::int8_type;
using int16be_t = big_endian::int16_type;
using int32be_t = big_endian::int32_type;
using int64be_t = big_endian::int64_type;

using uint8be_t = big_endian::uint8_type;
using uint16be_t = big_endian::uint16_type;
using uint32be_t = big_endian::uint32_type;
using uint64be_t = big_endian::uint64_type;

using float32be_t = big_endian::float32_type;
using float64be_t = big_endian::float64_type;

using charbe_t = big_endian::char_type;
using wcharbe_t = big_endian::wchar_type;
using char8be_t = big_endian::char8_type;
using char16be_t = big_endian::char16_type;
using char32be_t = big_endian::char32_type;

using wstringbe = std::basic_string<wcharbe_t>;
using u8stringbe = std::basic_string<char8be_t>;
using u16stringbe = std::basic_string<char16be_t>;
using u32stringbe = std::basic_string<char32be_t>;

using stringbe_view = std::basic_string_view<charbe_t>;
using wstringbe_view = std::basic_string_view<wcharbe_t>;
using u8stringbe_view = std::basic_string_view<char8be_t>;
using u16stringbe_view = std::basic_string_view<char16be_t>;
using u32stringbe_view = std::basic_string_view<char32be_t>;


/* little endian */

using little_endian = fixed_endian<false>;

using int8le_t = little_endian::int8_type;
using int16le_t = little_endian::int16_type;
using int32le_t = little_endian::int32_type;
using int64le_t = little_endian::int64_type;

using uint8le_t = little_endian::uint8_type;
using uint16le_t = little_endian::uint16_type;
using uint32le_t = little_endian::uint32_type;
using uint64le_t = little_endian::uint64_type;

using float32le_t = little_endian::float32_type;
using float64le_t = little_endian::float64_type;

using charle_t = little_endian::char_type;
using wcharle_t = little_endian::wchar_type;
using char8le_t = little_endian::char8_type;
using char16le_t = little_endian::char16_type;
using char32le_t = little_endian::char32_type;

using stringle = std::basic_string<charle_t>;
using wstringle = std::basic_string<wcharle_t>;
using u8stringle = std::basic_string<char8le_t>;
using u16stringle = std::basic_string<char16le_t>;
using u32stringle = std::basic_string<char32le_t>;

using stringle_view = std::basic_string_view<charle_t>;
using wstringle_view = std::basic_string_view<wcharle_t>;
using u8stringle_view = std::basic_string_view<char8le_t>;
using u16stringle_view = std::basic_string_view<char16le_t>;
using u32stringle_view = std::basic_string_view<char32le_t>;


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

/*
 * テスト
 */

#include <algorithm>
#include <cstdio>
#include <typeinfo>

static const uint16_t native_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static const uint16be_t big_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};
static const uint16le_t little_data_u16[] = {
    0x0123, 0x4567, 0x89ab, 0xcdef, 0xfedc, 0xba98, 0x7654, 0x3210,
};

static const uint32_t native_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static const uint32be_t big_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};
static const uint32le_t little_data_u32[] = {
    0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210,
};

static const uint64_t native_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static const uint64be_t big_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};
static const uint64le_t little_data_u64[] = {
    0x0123456789abcdefULL, 0xfedcba9876543210ULL,
};

static const float native_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32be_t big_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};
static float32le_t little_data_f32[] = {
    1.997417768961895490e-07,  // 0x3456789a
    3.704604248046875000e+03,  // 0x456789ab
    6.833586569216000000e+13,  // 0x56789abc
    1.075959298965006122e+33,  // 0x76543210
};

static const double native_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64be_t big_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};
static float64le_t little_data_f64[] = {
    1.4319418138097981587699937142696840e-56,   // 0x3456789abcdef012
    2.2764341833285411032321228800000000e+26,   // 0x456789abcdef0123
    3.6115365933711609499120338465022186e+108,  // 0x56789abcdef01234
    9.9364762676789442589659242262712990e+261,  // 0x76543210fedcba98
};

static void dumpbin(const char* msg, const void* s, size_t n)
{
    const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
    size_t x, y, ny = n & ~15, nr = n & 15;

    printf("address = %p: %s\n", s, msg);
    for (y = 0; y < ny; y += 16)
    {
        printf("   ");
        for (size_t x = 0; x < 16; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
    if (y < n)
    {
        printf("   ");
        for (x = 0; x < nr; x++)
            printf(" %02x", p[y + x]);
        printf("\n");
    }
}

template <typename T>
inline static void dumpbin(const char* msg, const T& s)
{
    dumpbin(msg, &s, sizeof(T));
}

template <typename T>
inline static void dumpbin(const char* msg, const std::basic_string<T>& s)
{
    dumpbin(msg, s.data(), s.size() * sizeof(T));
}

template <typename T>
inline static const char *gettypename()
{
    return typeid(T()).name();
}

template <typename T>
inline static const char *gettypename(const T v)
{
    return typeid(v).name();
}

template <typename T>
inline static const char *valuetypename()
{
    return typeid(T(0)).name();
}

template <typename T>
static void rtypename(const char* name, const T& x)
{

    int8_t i8 = 11;
    int16_t i16 = 12;
    int32_t i32 = 14;
    int64_t i64 = 18;
    uint8_t u8 = 21;
    uint16_t u16 = 22;
    uint32_t u32 = 24;
    uint64_t u64 = 28;
    float f32 = 1.5;
    double f64 = 2.25;

    char c8 = 1;
    wchar_t wc = 2;
    char16_t c16 = 2;
    char32_t c32 = 4;

    printf("%s = %s\n", name, gettypename(x));
    printf("    +i8:%s  +i16:%s  +i32:%s  +i64:%s\n",
           gettypename(x + i8), gettypename(x + i16),
           gettypename(x + i32), gettypename(x + i64));
    printf("    +u8:%s  +u16:%s  +u32:%s  +u64:%s\n",
           gettypename(x + u8), gettypename(x + u16),
           gettypename(x + u32), gettypename(x + u64));
    printf("    +c8:%s  +c16:%s  +c32:%s  +wc:%s\n",
           gettypename(x + c8), gettypename(x + c16),
           gettypename(x + c32), gettypename(x + wc));
    printf("    +f32:%s  +f64:%s\n",
           gettypename(x + f32), gettypename(x + f64));
    printf("\n");

#if 0
    T y = 0;

    printf("    +=i8:%s  +=i16:%s  +=i32:%s  +i64:%s\n",
           gettypename(y += i8), gettypename(y += i16),
           gettypename(y += i32), gettypename(y += i64));
    printf("    +=u8:%s  +=u16:%s  +=u32:%s  +=u64:%s\n",
           gettypename(y += u8), gettypename(y += u16),
           gettypename(y += u32), gettypename(y += u64));
    printf("    +=f32:%s  +=f64:%s\n",
           gettypename(y += f32), gettypename(y += f64));
    printf("    +=c8:%s  +=wc:%s\n",
           gettypename(y += c8), gettypename(y += wc));
    printf("\n");
#endif
}

/*
 *
 */

int main()
{
#ifdef __cplusplus
    printf("compiler: __cplusplus = %ld\n", __cplusplus);
#endif
#ifdef _MSVC_LANG
    printf("compiler: _MSVC_LANG = %ld\n", _MSVC_LANG);
#endif

    printf("endian: native = %s\n", native_endian::is_big ? "big" : "little");
    printf("\n");

    dumpbin("native_data_u16", native_data_u16);
    dumpbin("big_data_u16", big_data_u16);
    dumpbin("little_data_u16", little_data_u16);
    printf("\n");

    dumpbin("native_data_u32", native_data_u32);
    dumpbin("big_data_u32", big_data_u32);
    dumpbin("little_data_u32", little_data_u32);
    printf("\n");

    dumpbin("native_data_u64", native_data_u64);
    dumpbin("big_data_u64", big_data_u64);
    dumpbin("little_data_u64", little_data_u64);
    printf("\n");

    dumpbin("native_data_f32", native_data_f32);
    dumpbin("big_data_f32", big_data_f32);
    dumpbin("little_data_f32", little_data_f32);
    printf("\n");

    dumpbin("native_data_f64", native_data_f64);
    dumpbin("big_data_f64", big_data_f64);
    dumpbin("little_data_f64", little_data_f64);
    printf("\n");

    int16be_t i16be = 32;
    int32be_t i32be = 34;
    int64be_t i64be = 38;
    uint16be_t u16be = 42;
    uint32be_t u32be = 44;
    uint64be_t u64be = 48;
    float32be_t f32be = 4.25;
    float64be_t f64be = 8.125;

    int16le_t i16le = i16be;
    int32le_t i32le = i32be;
    int64le_t i64le = i64be;
    uint16le_t u16le = u16be;
    uint32le_t u32le = u32be;
    uint64le_t u64le = u64be;
    float32le_t f32le = f32be;
    float64le_t f64le = f64be;

    /**/

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    dumpbin("float32be_t", f32be); dumpbin("float32le_t", f32le);
    dumpbin("float64be_t", f64be); dumpbin("float64le_t", f64le);
    printf("\n");

    /**/

    u16be = 32; u32be = 34; u64be = 38;
    i16be = 42; i32be = 44; i64be = 48;
    u16le = 32; u32le = 34; u64le = 38;
    i16le = 42; i32le = 44; i64le = 48;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    u16be <<= 4; u32be <<= 4; u64be <<= 4;
    i16be <<= 4; i32be <<= 4; i64be <<= 4;
    u16le <<= 4; u32le <<= 4; u64le <<= 4;
    i16le <<= 4; i32le <<= 4; i64le <<= 4;

    dumpbin("i16be_t", i16be); dumpbin("i16le_t", i16le);
    dumpbin("u16be_t", u16be); dumpbin("u16le_t", u16le);
    dumpbin("i32be_t", i32be); dumpbin("i32le_t", i32le);
    dumpbin("u32be_t", u32be); dumpbin("u32le_t", u32le);
    dumpbin("i64be_t", i64be); dumpbin("i64le_t", i64le);
    dumpbin("u64be_t", u64be); dumpbin("u64le_t", u64le);
    printf("\n");

    /**/

    printf("compare:\n");
#define COMP(l,op,r)                            \
    printf("    %s(%d) %-2s %s(%d) : %s\n",     \
           #l, (int)l, #op, #r, (int)r,         \
           (l op r) ? "true" : "false")
    COMP(i16be, < , i16le); COMP(i16be, <=, i16le); COMP(i16be, ==, i16le);
    COMP(i16be, !=, i16le); COMP(i16be, >=, i16le); COMP(i16be, > , i16le);
    printf("\n");
    COMP(i32be, < , i16le); COMP(i32be, <=, i16le); COMP(i32be, ==, i16le);
    COMP(i32be, !=, i16le); COMP(i32be, >=, i16le); COMP(i32be, > , i16le);
    printf("\n");
    COMP(i32be, < , i64be); COMP(i32be, <=, i64be); COMP(i32be, ==, i64be);
    COMP(i32be, !=, i64be); COMP(i32be, >=, i64be); COMP(i32be, > , i64be);
    printf("\n");
    COMP(i32be, < , i64le); COMP(i32be, <=, i64le); COMP(i32be, ==, i64le);
    COMP(i32be, !=, i64le); COMP(i32be, >=, i64le); COMP(i32be, > , i64le);
    printf("\n");

    /**/

    printf("typeid:\n");
    printf("    int8_t:%s  int16_t:%s  int32_t:%s  int64_t:%s\n",
           valuetypename<int8_t>(), valuetypename<int16_t>(),
           valuetypename<int32_t>(), valuetypename<int64_t>());
    printf("    uint8_t:%s  uint16_t:%s  uint32_t:%s  uint64_t:%s\n",
           valuetypename<uint8_t>(), valuetypename<uint16_t>(),
           valuetypename<uint32_t>(), valuetypename<uint64_t>());
    printf("    float:%s  double:%s\n", valuetypename<float>(), valuetypename<double>());
    printf("    char:%s  wchar_t:%s\n", valuetypename<char>(), valuetypename<wchar_t>());
    printf("    int:%s  unsigned:%s\n", valuetypename<int>(), valuetypename<unsigned>());
    printf("    sizeof(wchar_t):%zd\n", sizeof(wchar_t));
    printf("\n");

    /**/

    rtypename("int16be_t", i16be); rtypename("int16le_t", i16le);
    rtypename("int32be_t", i32be); rtypename("int32le_t", i32le);
    rtypename("int64be_t", i64be); rtypename("int64le_t", i64le);

    rtypename("uint16be_t", u16be); rtypename("uint16le_t", u16le);
    rtypename("uint32be_t", u32be); rtypename("uint32le_t", u32le);
    rtypename("uint64be_t", u64be); rtypename("uint64le_t", u64le);

    rtypename("float32be_t", f32be); rtypename("float32le_t", f32le);
    rtypename("float64be_t", f64be); rtypename("float64le_t", f64le);

    /**/

    std::wstring ws(L"サンプルTEXT");
    wstringbe wsbe(ws.begin(), ws.end());
    wstringle wsle(ws.begin(), ws.end());
    dumpbin("wstring", ws);
    dumpbin("wstringbe", wsbe);
    dumpbin("wstringle", wsle);
    printf("\n");

    std::u16string u16s(ws.begin(), ws.end()); /* sizeof(wchar_t) != sizeof(char16_t) の場合は要注意 */
    u16stringbe u16sbe(u16s.begin(), u16s.end());
    u16stringle u16sle(u16s.begin(), u16s.end());
    dumpbin("u16string", u16s);
    dumpbin("u16stringbe", u16sbe);
    dumpbin("u16stringle", u16sle);
    printf("\n");

    std::u32string u32s(ws.begin(), ws.end()); /* sizeof(wchar_t) != sizeof(char32_t) の場合は要注意 */
    u32stringbe u32sbe(u32s.begin(), u32s.end());
    u32stringle u32sle(u32s.begin(), u32s.end());
    dumpbin("u32string", u32s);
    dumpbin("u32stringbe", u32sbe);
    dumpbin("u32stringle", u32sle);
    printf("\n");

    return 0;
}
```

```console:実行結果
$ clang++ -O3 -g0 -DNDEBUG=1 -std=c++20 endian-20.cpp -o endian-20
$ ./endian-20
compiler: __cplusplus = 202002
endian: native = little

address = 0x1001d4900: native_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32
address = 0x1001d4910: big_data_u16
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x1001d4920: little_data_u16
    23 01 67 45 ab 89 ef cd dc fe 98 ba 54 76 10 32

address = 0x1001d4930: native_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76
address = 0x1001d4940: big_data_u32
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x1001d4950: little_data_u32
    67 45 23 01 ef cd ab 89 98 ba dc fe 10 32 54 76

address = 0x1001d4960: native_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe
address = 0x1001d4970: big_data_u64
    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
address = 0x1001d4980: little_data_u64
    ef cd ab 89 67 45 23 01 10 32 54 76 98 ba dc fe

address = 0x1001d4990: native_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76
address = 0x1001d6000: big_data_f32
    34 56 78 9a 45 67 89 ab 56 78 9a bc 76 54 32 10
address = 0x1001d6030: little_data_f32
    9a 78 56 34 ab 89 67 45 bc 9a 78 56 10 32 54 76

address = 0x1001d49a0: native_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76
address = 0x1001d6010: big_data_f64
    34 56 78 9a bc de f0 12 45 67 89 ab cd ef 01 23
    56 78 9a bc de f0 12 34 76 54 32 10 fe dc ba 98
address = 0x1001d6040: little_data_f64
    12 f0 de bc 9a 78 56 34 23 01 ef cd ab 89 67 45
    34 12 f0 de bc 9a 78 56 98 ba dc fe 10 32 54 76

address = 0x7ff7bfd2f630: i16be_t
    00 20
address = 0x7ff7bfd2f636: i16le_t
    20 00
address = 0x7ff7bfd2f620: u16be_t
    00 2a
address = 0x7ff7bfd2f61e: u16le_t
    2a 00
address = 0x7ff7bfd2f628: i32be_t
    00 00 00 22
address = 0x7ff7bfd2f600: i32le_t
    22 00 00 00
address = 0x7ff7bfd2f618: u32be_t
    00 00 00 2c
address = 0x7ff7bfd2f604: u32le_t
    2c 00 00 00
address = 0x7ff7bfd2f610: i64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7bfd2f608: i64le_t
    26 00 00 00 00 00 00 00
address = 0x7ff7bfd2f5f8: u64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7bfd2f5f0: u64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7bfd2f5e8: float32be_t
    40 88 00 00
address = 0x7ff7bfd2f5e4: float32le_t
    00 00 88 40
address = 0x7ff7bfd2f5d0: float64be_t
    40 20 40 00 00 00 00 00
address = 0x7ff7bfd2f5d8: float64le_t
    00 00 00 00 00 40 20 40

address = 0x7ff7bfd2f630: i16be_t
    00 2a
address = 0x7ff7bfd2f636: i16le_t
    2a 00
address = 0x7ff7bfd2f620: u16be_t
    00 20
address = 0x7ff7bfd2f61e: u16le_t
    20 00
address = 0x7ff7bfd2f628: i32be_t
    00 00 00 2c
address = 0x7ff7bfd2f600: i32le_t
    2c 00 00 00
address = 0x7ff7bfd2f618: u32be_t
    00 00 00 22
address = 0x7ff7bfd2f604: u32le_t
    22 00 00 00
address = 0x7ff7bfd2f610: i64be_t
    00 00 00 00 00 00 00 30
address = 0x7ff7bfd2f608: i64le_t
    30 00 00 00 00 00 00 00
address = 0x7ff7bfd2f5f8: u64be_t
    00 00 00 00 00 00 00 26
address = 0x7ff7bfd2f5f0: u64le_t
    26 00 00 00 00 00 00 00

address = 0x7ff7bfd2f630: i16be_t
    02 a0
address = 0x7ff7bfd2f636: i16le_t
    a0 02
address = 0x7ff7bfd2f620: u16be_t
    02 00
address = 0x7ff7bfd2f61e: u16le_t
    00 02
address = 0x7ff7bfd2f628: i32be_t
    00 00 02 c0
address = 0x7ff7bfd2f600: i32le_t
    c0 02 00 00
address = 0x7ff7bfd2f618: u32be_t
    00 00 02 20
address = 0x7ff7bfd2f604: u32le_t
    20 02 00 00
address = 0x7ff7bfd2f610: i64be_t
    00 00 00 00 00 00 03 00
address = 0x7ff7bfd2f608: i64le_t
    00 03 00 00 00 00 00 00
address = 0x7ff7bfd2f5f8: u64be_t
    00 00 00 00 00 00 02 60
address = 0x7ff7bfd2f5f0: u64le_t
    60 02 00 00 00 00 00 00

compare:
    i16be(672) <  i16le(672) : false
    i16be(672) <= i16le(672) : true
    i16be(672) == i16le(672) : true
    i16be(672) != i16le(672) : false
    i16be(672) >= i16le(672) : true
    i16be(672) >  i16le(672) : false

    i32be(704) <  i16le(672) : false
    i32be(704) <= i16le(672) : false
    i32be(704) == i16le(672) : false
    i32be(704) != i16le(672) : true
    i32be(704) >= i16le(672) : true
    i32be(704) >  i16le(672) : true

    i32be(704) <  i64be(768) : true
    i32be(704) <= i64be(768) : true
    i32be(704) == i64be(768) : false
    i32be(704) != i64be(768) : true
    i32be(704) >= i64be(768) : false
    i32be(704) >  i64be(768) : false

    i32be(704) <  i64le(768) : true
    i32be(704) <= i64le(768) : true
    i32be(704) == i64le(768) : false
    i32be(704) != i64le(768) : true
    i32be(704) >= i64le(768) : false
    i32be(704) >  i64le(768) : false

typeid:
    int8_t:a  int16_t:s  int32_t:i  int64_t:x
    uint8_t:h  uint16_t:t  uint32_t:j  uint64_t:y
    float:f  double:d
    char:c  wchar_t:w
    int:i  unsigned:j
    sizeof(wchar_t):4

int16be_t = 14byteswap_valueIsE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int16le_t = s
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int32be_t = 14byteswap_valueIiE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int32le_t = i
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

int64be_t = 14byteswap_valueIxE
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +c8:x  +c16:x  +c32:x  +wc:x
    +f32:f  +f64:d

int64le_t = x
    +i8:x  +i16:x  +i32:x  +i64:x
    +u8:x  +u16:x  +u32:x  +u64:y
    +c8:x  +c16:x  +c32:x  +wc:x
    +f32:f  +f64:d

uint16be_t = 14byteswap_valueItE
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

uint16le_t = t
    +i8:i  +i16:i  +i32:i  +i64:x
    +u8:i  +u16:i  +u32:j  +u64:y
    +c8:i  +c16:i  +c32:j  +wc:i
    +f32:f  +f64:d

uint32be_t = 14byteswap_valueIjE
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +c8:j  +c16:j  +c32:j  +wc:j
    +f32:f  +f64:d

uint32le_t = j
    +i8:j  +i16:j  +i32:j  +i64:x
    +u8:j  +u16:j  +u32:j  +u64:y
    +c8:j  +c16:j  +c32:j  +wc:j
    +f32:f  +f64:d

uint64be_t = 14byteswap_valueIyE
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +c8:y  +c16:y  +c32:y  +wc:y
    +f32:f  +f64:d

uint64le_t = y
    +i8:y  +i16:y  +i32:y  +i64:y
    +u8:y  +u16:y  +u32:y  +u64:y
    +c8:y  +c16:y  +c32:y  +wc:y
    +f32:f  +f64:d

float32be_t = 14byteswap_valueIfE
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +c8:f  +c16:f  +c32:f  +wc:f
    +f32:f  +f64:d

float32le_t = f
    +i8:f  +i16:f  +i32:f  +i64:f
    +u8:f  +u16:f  +u32:f  +u64:f
    +c8:f  +c16:f  +c32:f  +wc:f
    +f32:f  +f64:d

float64be_t = 14byteswap_valueIdE
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +c8:d  +c16:d  +c32:d  +wc:d
    +f32:d  +f64:d

float64le_t = d
    +i8:d  +i16:d  +i32:d  +i64:d
    +u8:d  +u16:d  +u32:d  +u64:d
    +c8:d  +c16:d  +c32:d  +wc:d
    +f32:d  +f64:d

address = 0x60000074c000: wstring
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
address = 0x60000074c030: wstringbe
    00 00 30 b5 00 00 30 f3 00 00 30 d7 00 00 30 eb
    00 00 00 54 00 00 00 45 00 00 00 58 00 00 00 54
address = 0x60000074c060: wstringle
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00

address = 0x7ff7bfd2f59a: u16string
    b5 30 f3 30 d7 30 eb 30 54 00 45 00 58 00 54 00
address = 0x7ff7bfd2f552: u16stringbe
    30 b5 30 f3 30 d7 30 eb 00 54 00 45 00 58 00 54
address = 0x7ff7bfd2f56a: u16stringle
    b5 30 f3 30 d7 30 eb 30 54 00 45 00 58 00 54 00

address = 0x600000740000: u32string
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
address = 0x600000740030: u32stringbe
    00 00 30 b5 00 00 30 f3 00 00 30 d7 00 00 30 eb
    00 00 00 54 00 00 00 45 00 00 00 58 00 00 00 54
address = 0x600000740060: u32stringle
    b5 30 00 00 f3 30 00 00 d7 30 00 00 eb 30 00 00
    54 00 00 00 45 00 00 00 58 00 00 00 54 00 00 00
```

</div></details>


---

言語仕様の進化の恩恵を受けられるのはいいのだけど...