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

Есть ли способ обеспечить конкретную консистенцию для структуры C или С++?

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

Что бы я хотел, однако, если есть способ обеспечить конкретную сущность данной структуры. Есть ли какие-нибудь хорошие директивы компилятора или другие простые решения, помимо переписывания всего этого из большого количества макросов, манипулирующих на битполях?

Общее решение было бы неплохо, но я был бы доволен конкретным решением gcc.

Edit:

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

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

Невозможно изменить программное обеспечение для встроенного процессора. Источник доступен, поэтому я намерен использовать структуры из этой системы вместо того, чтобы начинать с нуля и оценивать все данные побайтно.

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

Это не должно быть реальным изменением в endianness. Даже если это просто интерфейс и физически все обрабатывается в собственной энтузиазме процессоров, это совершенно приемлемо для меня.

4b9b3361

Ответ 1

Я обычно обрабатываю это так:

#include <arpa/inet.h> // for ntohs() etc.
#include <stdint.h>

class be_uint16_t {
public:
        be_uint16_t() : be_val_(0) {
        }
        // Transparently cast from uint16_t
        be_uint16_t(const uint16_t &val) : be_val_(htons(val)) {
        }
        // Transparently cast to uint16_t
        operator uint16_t() const {
                return ntohs(be_val_);
        }
private:
        uint16_t be_val_;
} __attribute__((packed));

Аналогично для be_uint32_t.

Затем вы можете определить свою структуру следующим образом:

struct be_fixed64_t {
    be_uint32_t int_part;
    be_uint32_t frac_part;
} __attribute__((packed));

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

be_uint16_t x = 12;
x = x + 1; // Yes, this actually works
write(fd, &x, sizeof(x)); // writes 13 to file in big-endian form

Фактически, если вы скомпилируете этот фрагмент с любым достаточно хорошим компилятором на С++, вы должны найти, что он испускает константу "13" в формате big-endian.

С этими объектами представление в памяти является big-endian. Таким образом, вы можете создавать массивы из них, размещать их в структурах и т.д. Но когда вы идете работать на них, они магически бросаются в машинный endian. Обычно это одна инструкция на x86, поэтому она очень эффективна. Есть несколько контекстов, в которых вам нужно вручную отбрасывать:

be_uint16_t x = 37;
printf("x == %u\n", (unsigned)x); // Fails to compile without the cast

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

Ответ 2

Немного поздно для вечеринки, но с текущим GCC (тестируется в 6.2.1, где он работает, и 4.9.2, где он не реализован), наконец, есть способ объявить, что структура должна храниться в порядке X-endian byte.

Следующая тестовая программа:

#include <stdio.h>
#include <stdint.h>

struct __attribute__((packed, scalar_storage_order("big-endian"))) mystruct {
    uint16_t a;
    uint32_t b;
    uint64_t c;
};


int main(int argc, char** argv) {
    struct mystruct bar = {.a = 0xaabb, .b = 0xff0000aa, .c = 0xabcdefaabbccddee};

    FILE *f = fopen("out.bin", "wb");
    size_t written = fwrite(&bar, sizeof(struct mystruct), 1, f);
    fclose(f);
}

создает файл "out.bin", который вы можете проверить с помощью шестнадцатеричного редактора (например, hexdump -C out.bin). Если атрибут scalar_storage_order поддерживается, он будет содержать ожидаемый 0xaabbff0000aaabcdefaabbccddee в этом порядке и без отверстий. К сожалению, это, конечно, специфический компилятор.

Ответ 3

Нет, я так не думаю.

Endianness - это атрибут процессора, который указывает, представлены ли целые числа слева направо или справа налево, это не атрибут компилятора.

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

Ответ 4

Нет, нет такой возможности. Если он существует, что может заставить компиляторы генерировать избыточный/неэффективный код, поэтому С++ просто не поддерживает его.

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

Ответ 5

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

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

Рабочий процесс работает таким образом...

  • Прочитайте данные в исходной структуре.
  • Преобразуйте в "сырую структуру" значение "в версии памяти"
  • Работайте только с версией "в памяти"
  • Когда вы закончите работу над ним, преобразуйте "в память" обратно в "сырую структуру" и запишите его.

Мы считаем эту развязку полезной, потому что (но не ограничиваясь этим)...

  • Все конверсии расположены только в одном месте.
  • Меньше головных болей при проблемах с выравниванием памяти при работе с версией "в памяти".
  • Это облегчает перенос из одной арки в другую (меньше проблем с endian).

Надеемся, что эта развязка может быть полезна и для вашего приложения.

Ответ 6

Возможным инновационным решением могло бы быть использование интерпретатора C, такого как Ch и принудительное кодирование конца.

Ответ 7

Boost предоставляет endian buffers для этого.

Например:

#include <boost/endian/buffers.hpp>
#include <boost/static_assert.hpp>

using namespace boost::endian;

struct header {
    big_int32_buf_t     file_code;
    big_int32_buf_t     file_length;
    little_int32_buf_t  version;
    little_int32_buf_t  shape_type;
};
BOOST_STATIC_ASSERT(sizeof(h) == 16U);

Ответ 8

Может быть, не прямой ответ, но если вы прочитаете этот вопрос, можно надеяться, ответьте на некоторые из ваших проблем.

Ответ 9

Вы можете сделать структуру классом с геттерами и сеттерами для членов данных. Геттеры и сеттеры реализованы с чем-то вроде:

int getSomeValue( void ) const {
#if defined( BIG_ENDIAN )
    return _value;
#else
    return convert_to_little_endian( _value );
#endif
}

void setSomeValue( int newValue) {
#if defined( BIG_ENDIAN )
    _value = newValue;
#else
    _value = convert_to_big_endian( newValue );
#endif
}

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

Ответ 10

Существует представление данных для этого типа XDR. Посмотри на это. http://en.wikipedia.org/wiki/External_Data_Representation

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

XDR обычно используется в сетевых системах, так как им нужен способ перемещения данных в независимом от Endianness пути. Хотя ничто не говорит о том, что его нельзя использовать за пределами сетей.