2015-06-19 2 views
1

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

Пример

Рассмотрим структуру, состоящую из различных типов данных в области N -aligned памяти:

struct 
{ 
    float a; 
    uint8_t b; 
    uint32_t c; 
} s; 

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

void write_to_eeprom(uint32_t *data, uint32_t len); 

который берет указатель на данные, которые должны быть записаны как uint32_t*. Теперь мы хотели бы написать s в eeprom, используя эту функцию. Наивный подход состоял бы в том, чтобы сделать что-то вроде

write_to_eeprom((uint32_t*)&s, sizeof(s)/4); 

Но это явное нарушение строгого правила псевдонимов.

Второй пример

struct 
{ 
    uint32_t a; 
    uint8_t b; 
    uint32_t c; 
} s; 

В этом случае сглаживание (uint32_t*)&s не нарушает правила, так как указатель совместим с указателем на первый тип поля, который является законным. Но! Функция библиотеки может быть реализована так, что она выполняет некоторую арифметику указателя для итерации входных данных, тогда как эти арифметические результирующие указатели несовместимы с данными, на которые они указывают (например, data+1 является указателем типа uint32_t*, но это может указывать на поле uint8_t). Который снова является нарушением правила, как я понимаю.

Возможное решение?

Обертка проблематичная структура в виде объединения с массивами от желаемого типа:

union 
{ 
    struct_type s; 
    uint32_t array[sizeof(struct_type)/4]; 
} u; 

и передать u.array к библиотечной функции.

Это правильный способ сделать это? Это только правильный способ сделать это? Какие могут быть другие подходы?

+0

Очень простое решение: 'write_to_eeprom (char const * data, size_t len_times_four)'. –

+0

@KerrekSB Как я уже сказал, 'write_to_eeprom' - это библиотечная функция, которая не поддается контролю. И все же, приведение 'float *' в 'char *' является нарушением, не так ли ?. –

+0

@KerrekSB На самом деле нет, это не ... Все может быть сглажено символу 'char *'. –

ответ

2

Только примечание Я не совсем уверен, но может быть, что не всегда безопасно бросать uint8_t* в char* (here).

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

uint32_t dest[4] = {0}; 
memcpy(buffer, &s.a, sizeof(float)); 
memcpy(buffer+1, &s.b, sizeof(uint8_t)); 
memcpy(buffer+2, &s.c, sizeof(uint32_t)); 

write_to_eeprom(buffer, 3 /* Nr of elements */); 

Если вы хотите, чтобы скопировать элементы структуры в целочисленном массиве последовательно - вы можете сначала скопировать элемент структуры в массив байтов последовательно - и затем скопировать массив байтов в uint32_t массива.А также передайте количество байтов в качестве последнего параметра, который будет - sizeof(float)+sizeof(uint8_t)+sizeof(uint32_t)

+0

@ Юджин Ш .: Каково ваше мнение об этом решении? –

+0

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

+0

@EugeneSh .: Я думаю, что это было действительно безопасно. Я также упомянул и о том, что вы не хотите копировать отдельные элементы массива или выровнять все значения (вторая опция в моем ответе) –

0

Считайте, что запись в eeprom часто медленнее, иногда намного медленнее, чем запись в обычную память, что использование промежуточного буфера редко является перетаскиванием. Я понимаю, что это идет вразрез с this comment, но я чувствую, что это заслуживает внимания, поскольку он обрабатывает все другие C касается

Написать вспомогательную функцию, которая не имеет не выравнивание, сглаживание, ни размер вопросы

extern void write_to_eeprom(/* I'd expect const */ uint32_t *data, uint32_t len); 

// Adjust N per system needs 
#define BYTES_TO_EEPROM_N 16 

void write_bytes_to_eeprom(const void *ptr, size_t size) { 
    const unsigned char *byte_ptr = ptr; 
    union { 
    uint32_t data32[BYTES_TO_EEPROM_N/sizeof (uint32_t)]; 
    unsigned char data8[BYTES_TO_EEPROM_N]; 
    } u; 

    while (size >= BYTES_TO_EEPROM_N) { 
    memcpy(u.data8, byte_ptr, BYTES_TO_EEPROM_N); // ** 
    byte_ptr += BYTES_TO_EEPROM_N; 
    write_to_eeprom(u.data32, BYTES_TO_EEPROM_N/sizeof (uint32_t)); 
    size -= BYTES_TO_EEPROM_N; 
    } 

    if (size > 0) { 
    memcpy(u.data8, byte_ptr, size); 
    while (size % sizeof (uint32_t)) { 
     u.data8[size++] = 0; // zero fill 
    } 
    write_to_eeprom(u.data32, (uint32_t) size); 
    } 
} 

// usage - very simple 
write_bytes_to_eeprom(&s, sizeof s); 

** Может использовать memcpy(u.data32, byte_ptr, BYTES_TO_EEPROM_N); для обработки @zwol issue.

Смежные вопросы