هل هناك طريقة للقيام بتأكيد وقت الترجمة على نمط 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_STATIC_ASSERT(!BIG_ENDIAN);، أو static_assert في ج ++ 0X. والسبب وأعتقد أن هذا هو لأن endian'nes خاصية إذا بيئة تنفيذ الخاصة بك. ومع ذلك، يعتبر static_assert في وقت الترجمة.

وأقترح أن ننظر إلى رمز من جديد <م> GNU الذهب ELF رابط. إيان لانس تايلور، مؤلفها، تستخدم قوالب لتحديد endianness الصحيح في وقت الترجمة، لضمان الأداء الأمثل في وقت التشغيل. وinstantiates explicitely كل endians ممكن، حتى انه لا يزال لديه تجميع منفصل (وليس كل القوالب في الرؤوس) من تعريف القالب والإعلان. كود له ممتاز.

تعتمد هذه الإجابة على المواصفات التالية (وهذا للتوضيح):

لغة:C++ الإصدار 17، 64 بت
المجمعون:g++ v8 (مجموعة مترجمات GNU https://www.gnu.org/software/gcc/) وسلسلة أدوات MingW 8.1.0 (https://sourceforge.net/projects/mingw-w64/files/)
أنظمة التشغيل:لينكس منت وويندوز

يمكن استخدام السطرين التاليين من التعليمات البرمجية للكشف بنجاح عن endianness المعالج:

const uint8_t IsLittleEndian = char (0x0001);

أو

#define IsLittleEndian char (0x0001)

تستفيد هاتان الجوهرتان السحريتان الصغيرتان من كيفية قيام المعالج بتخزين قيمة 16 بت في الذاكرة.

في معالج "Little Endian"، مثل شرائح Intel وAMD، يتم تخزين قيمة 16 بت في [low order/least significant byte][high order/most significant byte] الموضة (الأقواس تمثل بايت في الذاكرة).

في معالج "Big Endian"، مثل شرائح PowerPC وSun Sparc وIBM S/390، يتم تخزين قيمة 16 بت في ملف [high order/most significant byte][low order/least significant byte] موضة.

على سبيل المثال، عندما نقوم بتخزين قيمة 16 بت (2 بايت)، دعنا نقول 0x1234, ، إلى C++ uint16_t (النوع المحدد في C++ v11 والإصدارات الأحدث https://en.cppreference.com/w/cpp/types/integer) متغير الحجم على معالج "Little Endian"، ثم انظر إلى كتلة الذاكرة التي تم تخزين القيمة فيها، وستجد تسلسل البايت، [34][12].

على "معالج Endian الكبير"، 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

لقد كتبت هذا العرض التوضيحي باستخدام سلسلة أدوات GNU C++ في Linux Mint وليس لدي الوسائل للاختبار في النكهات الأخرى من C++ مثل Visual Studio أو سلسلة أدوات MingW، لذلك لا أعرف ما هو المطلوب لتجميعها فيها، ولا هل يمكنني الوصول إلى Windows في الوقت الحالي.

ومع ذلك، قام أحد أصدقائي باختبار الكود باستخدام MingW، 64 بت (x86_64-8.1.0-release-win32-seh-rt_v6-rev0) وكان به أخطاء.وبعد قليل من البحث، اكتشفت أنني بحاجة إلى إضافة السطر #define __STDC_FORMAT_MACROS في الجزء العلوي من الكود ليتم تجميعه مع MingW.

الآن بعد أن أصبح بإمكاننا أن نرى بصريًا كيف يتم تخزين قيمة 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>

عندما نقوم بتحويل قيمة 16 بت 0x0001 إلى حرف (8 بت) مع المقتطف char (0x0001), ، يستخدم المترجم إزاحة الذاكرة الأولى لقيمة 16 بت للقيمة الجديدة.إليك مخططًا آخر يوضح ما يحدث على معالجات "Little Endian" و"Big Endian":

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

كما ترون، يمكننا بسهولة تحديد endianness المعالج.


تحديث:

لا أستطيع اختبار العرض التوضيحي أعلاه على معالج "Big Endian"، لذلك قمت ببناء الكود على المعلومات التي وجدتها على الويب.شكرًا لـ 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 ثم يقرأ البايت الأول في ذاكرة المتغيرات.كما هو موضح في الإخراج الموضح أعلاه، في معالجات "Little Endian"، ستكون القيمة 0x01.في معالجات "Big Endian"، ستكون القيمة 0x00.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top