32

テンプレート化された低レベルのシリアル化コードがいくつかあり、コンパイル時にシステムのエンディアンを明らかに知る必要があります(テンプレートはシステムのエンディアンに基づいて特殊化されているため)。

現在、いくつかのプラットフォーム定義を含むヘッダーがありますが、テンプレート化されたテスト(static_assertやboost_ifなど)を使用してエンディアンについてアサーションを作成する必要があります。私のコードである理由は、さまざまなマシン、多くの専門ベンダー、そしておそらく2008年には存在しないデバイスでコンパイルして実行する必要があるため、そのヘッダー年に何を入れる必要があるかを実際に推測することはできません道を下って。また、コードベースの予想寿命は約10年です。だから私は永遠にコードに従うことはできません。

うまくいけば、これで私の状況が明らかになります。

では、ベンダー固有の定義に依存せずにエンディアンを判断できるコンパイル時テストを知っている人はいますか?

4

4 に答える 4

21

autoconf を使用している場合は、AC_C_BIGENDIAN動作がかなり保証されているマクロを使用できます (WORDS_BIGENDIANデフォルトで定義を設定します) 。

あるいは、次のようなもの (autoconf から取得) を試して、おそらく最適化されないテストを取得することもできます (少なくとも GCC は他のブランチを削除します)。

int is_big_endian()
{
    union {
        long int l;
        char c[sizeof (long int)];
    } u;

    u.l = 1;

    if (u.c[sizeof(long int)-1] == 1)
    {
        return 1;
    }
    else
        return 0;
}
于 2008-11-11T10:28:54.023 に答える
18

コンパイル時にこれを行うための移植可能な方法はありません。おそらく、 Boost エンディアンマクロを使用するか、それらが使用するメソッドをエミュレートするのが最善の策です。

于 2008-11-11T06:38:55.843 に答える
5

うーん、それは興味深い質問です。私の賭けは、これは不可能だということです。BOOST_STATIC_ASSERT(!BIG_ENDIAN);マクロを使い続けて、またはstatic_assertc++0xを使用する必要があると思います。これは、実行環境の場合、エンディアンがプロパティであるためだと思います。ただし、static_assertはコンパイル時に考慮されます。

新しいGNUゴールドELFリンカーのコードを調べることをお勧めします。著者のIanLanceTaylorは、テンプレートを使用してコンパイル時に適切なエンディアンを選択し、実行時に最適なパフォーマンスを確保しました。彼はすべての可能なエンディアンを明示的にインスタンス化するため、テンプレートの定義と宣言を個別にコンパイルできます(ヘッダー内のすべてのテンプレートではありません)。彼のコードは素晴らしいです。

于 2008-11-11T06:33:23.640 に答える
-1

この回答は、次の仕様に基づいています (これは明確にするためです)。

言語: C++ v17、64 ビット
コンパイラ: g++ v8 (GNU コンパイラ コレクションhttps://www.gnu.org/software/gcc/ ) & MingW 8.1.0 ツールチェーン ( https://sourceforge.net/projects/mingw -w64/files/ )
OS: Linux Mint & Windows

次の 2 行のコードを使用すると、プロセッサのエンディアンを正しく検出できます。

const uint8_t IsLittleEndian = char (0x0001);

また

#define IsLittleEndian char (0x0001)

これら 2 つの小さな魔法のステートメント gem は、プロセッサが 16 ビット値をメモリに格納する方法を利用しています。

Intel や AMD チップセットのような「リトル エンディアン」プロセッサでは、16 ビット値が一定の形式で格納され[low order/least significant byte][high order/most significant byte]ます (括弧はメモリ内の 1 バイトを表します)。

PowerPC、Sun Sparc、IBM S/390 チップセットなどの「ビッグ エンディアン」プロセッサでは、16 ビット値が一定の形式で格納され[high order/most significant byte][low order/least significant byte]ます。

たとえば、16 ビット (2 バイト) の値を0x1234C++ uint16_t(C++ v11 で定義された型、およびそれ以降はhttps://en.cppreference.com/w/cpp/types/integer )に格納するとします。変数を「リトルエンディアン」プロセッサで使用し、値が格納されているメモリ ブロックを調べると、バイト シーケンス[34][12].

「ビッグ エンディアン プロセッサ」では、0x1234値は として保存され[12][34]ます。

さまざまなサイズの C++ 整数変数がリトル エンディアン プロセッサとビッグ エンディアン プロセッサのメモリに格納される方法を示すための簡単なデモを次に示します。

#define __STDC_FORMAT_MACROS // Required for the MingW toolchain
#include <iostream>
#include <inttypes.h>

const uint8_t IsLittleEndian = char (0x0001);
//#define IsLittleEndian char (0x0001)

std::string CurrentEndianMsg;
std::string OppositeEndianMsg;

template <typename IntegerType>
void PrintIntegerDetails(IntegerType IntegerValue)
{
    uint16_t SizeOfIntegerValue = sizeof(IntegerValue);
    int8_t i;

    std::cout << "Integer size (in bytes): " << SizeOfIntegerValue << "\n";
    std::cout << "Integer value (Decimal): " << IntegerValue << "\n";
    std::cout << "Integer value (Hexidecimal): ";

    switch (SizeOfIntegerValue)
    {
        case 2: printf("0x%04X\n", (unsigned int) IntegerValue);
                break;
        case 4: printf("0x%08X\n", (unsigned int) IntegerValue);
                break;
        case 8: printf("0x%016" PRIX64 "\n", (uint64_t) IntegerValue);
                break;
    }

    std::cout << "Integer stored in memory in byte order:\n";
    std::cout << "        " << CurrentEndianMsg << " processor [current]: ";

    for(i = 0; i < SizeOfIntegerValue; i++)https://stackoverflow.com/qhttps://stackoverflow.com/questions/280162/is-there-a-way-to-do-a-c-style-compile-time-assertion-to-determine-machines-e/54175491#54175491uestions/280162/is-there-a-way-to-do-a-c-style-compile-time-assertion-to-determine-machines-e/54175491#54175491
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n        " << OppositeEndianMsg << " processor  [simulated]: ";

    for(i = SizeOfIntegerValue - 1; i >= 0; i--)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n\n";
}


int main()
{
    uint16_t ValueUInt16a = 0x0001;
    uint16_t ValueUInt16b = 0x1234;
    uint32_t ValueUInt32a = 0x00000001;
    uint32_t ValueUInt32b = 0x12345678;
    uint64_t ValueUInt64a = 0x0000000000000001;
    uint64_t ValueUInt64b = 0x123456789ABCDEF0;

    std::cout << "Current processor endianness: ";

    switch (IsLittleEndian) {
        case 0: CurrentEndianMsg = "Big Endian";
                OppositeEndianMsg = "Little Endian";
                break;
        case 1: CurrentEndianMsg = "Little Endian";
                OppositeEndianMsg = "Big Endian";
                break;
    }

    std::cout << CurrentEndianMsg << "\n\n";

    PrintIntegerDetails(ValueUInt16a);
    PrintIntegerDetails(ValueUInt16b);
    PrintIntegerDetails(ValueUInt32a);
    PrintIntegerDetails(ValueUInt32b);
    PrintIntegerDetails(ValueUInt64a);
    PrintIntegerDetails(ValueUInt64b);

    return 0;
}

私のマシンでのデモの出力は次のとおりです。

Current processor endianness: Little Endian

Integer size (in bytes): 2
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x0001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00
        Big Endian processor  [simulated]: 00 01

Integer size (in bytes): 2
Integer value (Decinal): 4660
Integer value (Hexidecimal): 0x1234
Integer stored in memory in byte order:
        Little Endian processor [current]: 34 12
        Big Endian processor  [simulated]: 12 34

Integer size (in bytes): 4
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x00000001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00 00 00
        Big Endian processor  [simulated]: 00 00 00 01

Integer size (in bytes): 4
Integer value (Decinal): 305419896
Integer value (Hexidecimal): 0x12345678
Integer stored in memory in byte order:
        Little Endian processor [current]: 78 56 34 12
        Big Endian processor  [simulated]: 12 34 56 78

Integer size (in bytes): 8
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x0000000000000001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00 00 00 00 00 00 00
        Big Endian processor  [simulated]: 00 00 00 00 00 00 00 01

Integer size (in bytes): 8
Integer value (Decinal): 13117684467463790320
Integer value (Hexidecimal): 0x123456789ABCDEF0While the process
Integer stored in memory in byte order:
        Little Endian processor [current]: F0 DE BC 9A 78 56 34 12
        Big Endian processor  [simulated]: 12 34 56 78 9A BC DE F0

Linux Mint の GNU C++ ツールチェーンを使用してこのデモを作成しましたが、Visual Studio や MingW ツールチェーンなどの他のフレーバーの C++ でテストする手段がありません。現時点で Windows にアクセスできますか。

しかし、私の友人が MingW、64 ビット (x86_64-8.1.0-release-win32-seh-rt_v6-rev0) でコードをテストしたところ、エラーが発生しました。少し調査した後、#define __STDC_FORMAT_MACROSMingW でコンパイルするには、コードの先頭に行を追加する必要があることがわかりました。

16 ビット値がメモリにどのように格納されるかを視覚的に確認できるようになったので、それを利用してプロセッサのエンディアンを判断する方法を見てみましょう。

16 ビット値がメモリに格納される方法を視覚化するのに少し役立つように、次の図を見てみましょう。

16-Bit Value (Hex):  0x1234

Memory Offset:       [00] [01]
                     ---------
Memory Byte Values:  [34] [12]  <Little Endian>
                     [12] [34]  <Big Endian>

================================================

16-Bit Value (Hex):  0x0001

Memory Offset:       [00] [01]
                     ---------
Memory Byte Values:  [01] [00]  <Little Endian>
                     [00] [01]  <Big Endian>

0x0001snippet を使用して 16 ビット値を char (8 ビット)に変換するchar (0x0001)と、コンパイラは新しい値に 16 ビット値の最初のメモリ オフセットを使用します。「リトルエンディアン」プロセッサと「ビッグ エンディアン」プロセッサの両方で何が起こるかを示す別のグラフを次に示します。

Original 16-Bit Value: 0x0001

Stored in memory as: [01][00]  <-- Little Endian
                     [00][01]  <-- Big Endian

Truncate to char:    [01][xx]  <-- Little Endian
                     [01]      Final Result
                     [00][xx]  <-- Big Endian
                     [00]      Final Result

ご覧のとおり、プロセッサのエンディアンを簡単に判断できます。


アップデート:

「ビッグ エンディアン」プロセッサで上記のデモをテストできないため、Web で見つけた情報に基づいてコードを作成しました。私に明白なことを指摘してくれたMMに感謝します。

エンディアンまたはプロセッサを正しくテストするために、デモ コード (以下を参照) を更新しました。

#define __STDC_FORMAT_MACROS // Required for the MingW toolchain
#include <iostream>
#include <inttypes.h>

std::string CurrentEndianMsg;
std::string OppositeEndianMsg;

template <typename IntegerType>
void PrintIntegerDetails(IntegerType IntegerValue)
{
    uint16_t SizeOfIntegerValue = sizeof(IntegerValue);
    int8_t i;

    std::cout << "Integer size (in bytes): " << SizeOfIntegerValue << "\n";
    std::cout << "Integer value (Decimal): " << IntegerValue << "\n";
    std::cout << "Integer value (Hexidecimal): ";

    switch (SizeOfIntegerValue)
    {
        case 2: printf("0x%04X\n", (unsigned int) IntegerValue);
                break;
        case 4: printf("0x%08X\n", (unsigned int) IntegerValue);
                break;
        case 8: printf("0x%016" PRIX64 "\n", (uint64_t) IntegerValue);
                break;
    }

    std::cout << "Integer stored in memory in byte order:\n";
    std::cout << "        " << CurrentEndianMsg << " processor [current]: ";

    for(i = 0; i < SizeOfIntegerValue; i++)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n        " << OppositeEndianMsg << " processor  [simulated]: ";

    for(i = SizeOfIntegerValue - 1; i >= 0; i--)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n\n";
}


int main()
{
    uint16_t ValueUInt16a = 0x0001;
    uint16_t ValueUInt16b = 0x1234;
    uint32_t ValueUInt32a = 0x00000001;
    uint32_t ValueUInt32b = 0x12345678;
    uint64_t ValueUInt64a = 0x0000000000000001;
    uint64_t ValueUInt64b = 0x123456789ABCDEF0;

    uint16_t EndianTestValue = 0x0001;
    uint8_t IsLittleEndian = ((unsigned char*) &EndianTestValue)[0];

    std::cout << "Current processor endianness: ";

    switch (IsLittleEndian) {
        case 0: CurrentEndianMsg = "Big Endian";
                OppositeEndianMsg = "Little Endian";
                break;
        case 1: CurrentEndianMsg = "Little Endian";
                OppositeEndianMsg = "Big Endian";
                break;
    }

    std::cout << CurrentEndianMsg << "\n\n";

    PrintIntegerDetails(ValueUInt16a);
    PrintIntegerDetails(ValueUInt16b);
    PrintIntegerDetails(ValueUInt32a);
    PrintIntegerDetails(ValueUInt32b);
    PrintIntegerDetails(ValueUInt64a);
    PrintIntegerDetails(ValueUInt64b);

    return 0;
}

この更新されたデモは、16 ビット値を作成0x0001し、変数メモリの最初のバイトを読み取ります。上記の出力に見られるように、「リトル エンディアン」プロセッサでは、値は 0x01 になります。「ビッグ エンディアン」プロセッサでは、値は 0x00 になります。

于 2019-01-14T03:37:25.723 に答える