Подтвердить что ты не робот

Тестирование кода на С++ для независимости от Endian

Как я могу проверить или проверить код на С++ для независимости от endian? Он уже реализован, я бы просто хотел убедиться, что он работает как на платформах с маленькими, так и на больших платформах.

Я могу написать модульные тесты и запустить их на целевых платформах, но у меня нет оборудования. Возможно, эмуляторы?

Могут ли выполняться проверки времени компиляции?

4b9b3361

Ответ 1

Если у вас есть доступ к Mac на базе x86, вы можете воспользоваться тем фактом, что Mac OS X имеет встроенную эмуляцию PowerPC, а также поддержку инструмента для разработчиков как для x86 (little endian), так и для PowerPC (big endian). Это позволяет вам скомпилировать и запустить исполняемый файл большого и меньшего размера на той же платформе, например.

$ gcc -arch i386 foo.c -o foo_x86 # build little endian x86 executable
$ gcc -arch ppc foo.c -o foo_ppc  # build big endian PowerPC executable

Сконструировав исполняемые файлы с большими и конечными значениями, вы можете запускать все те модульные тесты, которые у вас есть на обоих, что позволит уловить некоторые классы проблем, связанных с контентом, и вы также можете сравнить любые данные, сгенерированные исполняемыми файлами (файлы, сетевые пакеты, что угодно) - это должно совпадать.

Ответ 2

Вы можете настроить среду выполнения в противоположном endianness, используя qemu. Например, если у вас есть доступ к аппаратным средствам little-endian amd64 или i386, вы можете настроить qemu для эмуляции платформы PowerPC Linux, запустите там свой код.

Ответ 3

Я прочитал рассказ, который использовал Flint (Flexible Lint) для диагностики таких ошибок.

Больше не знаю специфику, но позвольте мне вернуться к истории:

http://www.datacenterworks.com/stories/flint.html

Пример: Диагностика ошибок Endianness

В недавнем взаимодействии мы переносили код из старого Sequent в SPARC, а после конкретных указательных вопросов, которые мы обсуждали в "Истории глупостей и ошибок" , нам нужно было искать другие проблемы с нулевым указателем, а также ошибки endian-ness.

Ответ 4

Я бы предложил адаптировать метод кодирования, который позволяет избежать проблемы вместе.

Во-первых, вы должны понять, в какой ситуации возникает проблема endianess. Затем либо найдите закон, способный конъюнктивно-агностически написать это, либо изолировать код.

Например, типичная проблема, при которой могут возникать проблемы с endianess, - это когда вы используете обращения к памяти или объединения для выделения частей большего значения. Конкретно, избегайте:

long x;
...
char second_byte = *(((char *)&x) + 1);

Вместо этого напишите:

long x;
...
char second_byte = (char)(x >> 8)

Конкатенация, это один из моих фаворитов, так как многие люди склонны думать, что вы можете делать это только с помощью странных трюков. Не делайте этого:

union uu
{
  long x;
  unsigned short s[2];
};
union uu u;
u.s[0] = low;
u.s[1] = high;
long res = u.x;       

Вместо этого напишите:

long res = (((unsigned long)high) << 16) | low

Ответ 5

Я могу написать модульные тесты и запустить их на целевых платформах, но у меня нет оборудования.

Вы можете настроить свой дизайн, чтобы модульные тесты легко запускались независимо от наличия аппаратного обеспечения. Вы можете сделать это с помощью инъекции зависимостей. Я могу абстрагироваться от таких вещей, как аппаратные интерфейсы, предоставляя базовый класс интерфейса, с которым тестирует код, который я тестирую.

class IHw
{
public:
    virtual void SendMsg1(const char* msg, size_t size) = 0;
    virtual void RcvMsg2(Msg2Callback* callback) = 0;
     ...
};

Тогда у меня может быть конкретная реализация, которая фактически говорит с аппаратным обеспечением:

class CHw : public IHw
{
public:
    void SendMsg1(const char* msg, size_t size);
    void RcvMsg2(Msg2Callback* callback);
};

И я могу сделать тестовую версию заглушки:

class CTestHw : public IHw
{
public:
    void SendMsg1(const char* msg, size_t);
    void RcvMsg2(Msg2Callback* callback);
};

Тогда мой реальный код может дать нам конкретный Hw, но я смогу имитировать его в тестовом коде с помощью CTestHw.

class CSomeClassThatUsesHw
{
public:
   void MyCallback(const char* msg, size_t size)
   {
       // process msg 2
   }
   void DoSomethingToHw()
   {
       hw->SendMsg1();
       hw->RcvMsg2(&MyCallback);
   }
private:
    IHw* hw; 
}

Ответ 6

Это имеет значение только в том случае, если вы сохраняете целочисленные данные в двоичном формате машины.

Edit:

Как вы тестируете приложение для работы с endianess? В принципе вы не можете (не динамически). Приложение будет работать с любым состоянием, которое вы ему даете. Единственное, что вы можете проверить, это состояние сохраненных данных. Вы можете проверить, что сохраняемые данные (вывод) находятся в определенном формате с учетом конкретного ввода (данного хорошего состояния).

Решение просто не делать этого.

Таким образом, любая точка, где сохраняются двоичные данные (в файл/сетевой поток и т.д.), убедитесь, что:

  • Это не двоичный
  • Или, если он является двоичным, сделать его машинным агностиком.

Чтобы сделать его агностиком, просто используйте ntohl() и family для преобразования целых чисел в/из порядка и размера сетевого байта. Тогда вы всегда будете последовательны.

Ответ 7

ИМО, единственный ответ, близкий к правильному, - это Мартин. Нет проблем с контентом, если вы не общаетесь с другими приложениями в двоичных или чтениях/записи двоичных файлов. То, что происходит на маленькой конечной машине, остается на маленькой конечной машине, если все постоянные данные представлены в виде потока символов (например, пакеты ASCII, входные файлы ASCII, выходные файлы ASCII).

Я делаю это скорее ответом, чем комментарием к Мартину, потому что я предлагаю вам рассмотреть возможность сделать что-то отличное от того, что предложил Мартин. Учитывая, что доминирующая машинная архитектура мало ориентирована, в то время как порядок сети является большим, многие преимущества возникают, если вы можете избежать обмена байтами в целом. Решение заключается в том, чтобы ваше приложение могло иметь дело с неправильными входами. Заставьте протокол связи начать с какого-то идентификационного пакета машины. Используя эту информацию, ваша программа может узнать, нужно ли ей байтовать свопинг последующих входящих пакетов или оставить их как есть. Эта же концепция применяется, если заголовок ваших двоичных файлов имеет некоторый индикатор, который позволяет определить достоверность этих файлов. Используя такую ​​архитектуру, ваши приложения могут писать в собственном формате и могут знать, как обращаться с входами, которые не находятся в собственном формате.

Ну, почти. Существуют и другие проблемы с бинарным обменом/бинарными файлами. Одной из таких проблем являются данные с плавающей запятой. Стандарт с плавающей запятой IEEE не говорит ничего о том, как хранятся данные с плавающей запятой. Он ничего не говорит о байтовом порядке, ничего о том, присутствует ли значение до или после экспоненты, ничего о порядке хранения бит хранимого экспонента и значимости. Это означает, что у вас могут быть две разные машины с одинаковой степенью точности, которые следуют стандарту IEEE, и у вас все еще могут быть проблемы с передачей данных с плавающей запятой в виде двоичных файлов.

Еще одна проблема, не столь распространенная сегодня, заключается в том, что сущность не двоичная. Есть и другие варианты, чем большие и маленькие. К счастью, дни компьютеров, которые хранят вещи в порядке 2143 (в отличие от порядка 1234 или 4321), в значительной степени отстают от нас, если вы не имеете дело со встроенными системами.

Нижняя линия: Если вы имеете дело с почти однородным набором компьютеров, с одним или двумя странными (но не слишком странными), вы можете подумать об избежании сетевого порядка. Если в домене есть машины с несколькими архитектурами, некоторые из них очень странные, вам, возможно, придется обратиться к lingua franca сетевого порядка. (Но будьте осторожны, что этот lingua franca не полностью разрешает проблему с плавающей запятой.)