Тестирование кода на C ++ для endian-независимости - PullRequest
30 голосов
/ 20 июня 2011

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

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

Есть ли проверки времени компиляции, которые можно сделать?

Ответы [ 7 ]

15 голосов
/ 20 июня 2011

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

$ 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

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

10 голосов
/ 20 июня 2011

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

7 голосов
/ 20 июня 2011

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

Не знаю больше подробностей, но позвольте мне отсканировать историю для вас:

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

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

При недавнем задании мы переносили код со старого Sequent на SPARC и после определенного указателяпроблемы, которые мы обсуждали в Story of Thud and Blunder, нам нужно было искать другие проблемы с нулевым указателем, а также ошибки endian-ness.

4 голосов
/ 20 июня 2011

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

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

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

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
1 голос
/ 20 июня 2011

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

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

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; 
}
0 голосов
/ 20 июня 2011

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

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

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

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

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

0 голосов
/ 20 июня 2011

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

Редактировать:

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

Решение состоит просто в том, чтобы этого не делать.

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

  • Это не двоичные данные
  • Или, если они двоичные, сделайте их независимыми от компьютера.

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

...