C ++スタイルのコンパイル時アサーションを実行してマシンのエンディアンを判断する方法はありますか?

StackOverflow https://stackoverflow.com/questions/280162

質問

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

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

うまくいけば、私の状況が明確になります。

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

役に立ちましたか?

解決

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;
}

他のヒント

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

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

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

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

  

言語:C ++ v17、64ビット
  コンパイラ:g ++ v8(GNU Compiler Collection 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つの小さな魔法の宝石は、プロセッサがメモリに16ビット値を格納する方法を利用します。

「リトルエンディアン」でIntelおよびAMDチップセットのようなプロセッサ、16ビット値は [下位/最下位バイト] [上位/最上位バイト] の形式で格納されます(括弧はメモリ内のバイトを表します)。

「ビッグエンディアン」でPowerPC、Sun Sparc、IBM S / 390チップセットなどのプロセッサでは、16ビット値が [上位/最上位バイト] [下位/最下位バイト] の形式で格納されます。

たとえば、16ビット(2バイト)の値、たとえば 0x1234 をC ++ 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)でコードをテストしたところ、エラーが発生しました。少し調査した結果、MingWでコンパイルするには、コードの先頭に #define __STDC_FORMAT_MACROS という行を追加する必要があることがわかりました。

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>

スニペット char(0x0001)で16ビット値 0x0001 をchar(8ビット)に変換すると、コンパイラは次の最初のメモリオフセットを使用します。新しい値の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

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


更新:

「ビッグエンディアン」で上記のデモをテストすることはできません。プロセッサなので、ウェブで見つけた情報に基づいてコードを作成しました。私に明らかなことを指摘してくれたM.Mに感謝します。

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

#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になります。 「ビッグエンディアン」でプロ

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top