Определите блок данных в C с помощью «указателей»

0

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

Структура выглядит примерно так в байтах

[Option] [VariableByte1] [VariableByte2] [VariableByte3] [VariableByte4] [MetaDataOffsetByte1] [MetaDataOffsetByte1]... (количество определений).... [MetadataFormat] [MinValue] [MaxValue]... (количество определений)....

В псевдо C первая часть может быть реализована как

#define OPTIONA 0x33
#define VAR(name) (&Name & 0xFF), ((&Name >> 8) & 0xFF)

#define METAFORMAT1 0x2
#define MAKEINT(value) (value & 0xFF), ((value >> 8) & 0xFF)
#define OFFSET /*Some preprocessor magic*/
char data[] =
{
 OPTION, VAR(someVariable), OFFSET(meta1)
 OPTION, VAR(someVariable), OFFSET(meta1),
 OPTION, VAR(someVariable), OFFSET(meta2),
meta1:
 METAFORMAT1, MAKEINT(0), MAKEINT(99),
meta2:
 METAFORMAT2, MAKEINT(-99), MAKEINT(0),
}

Теперь мы искали реализацию "Some preprocessor magic".

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

Некоторые факты окружающей среды:

  • Мы используем компилятор keil-arm-mdk
  • Он компилирует код C со стандартными настройками C++
  • Мы хотим определить байт-паттерн в памяти (приложение не взаимодействует с этой структурой
  • 3
    с или с ++ ? Кроме того, какой компилятор?
  • 0
    Код является кодом C, скомпилированным с помощью компилятора C / C ++. Желательно универсальное решение, но мы используем компилятор keil-mdk-arm.
Показать ещё 6 комментариев
Теги:
c-preprocessor

1 ответ

1
Лучший ответ

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

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

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

Если вы не хотите делать одно из вышесказанного, вы можете рассмотреть что-то в этом направлении:

#define BYTE(i) ((i) & 0xff)
#define LE2(i)  BYTE(i), BYTE((i) >> 8)
#define LE4(i)  BYTE(i), BYTE((i) >> 8), BYTE((i) >> 16), BYTE((i) >> 24)

/* The number of bytes in a variable entry */
#define VAR_SIZE    7
/*
 * The bytes of a variable entry.  The entries for one data block must appear
 * one per line on consecutive lines, without any intervening lines, by the
 * metadata.
 * - option is the option code
 * - var is the value to be stored in the entry (as a variable or a literal)
 * - meta is the 1-based index of the metadata format entry for this variable
 * - start is the source line number of the first variable entry
 * - nvars is the total number of variable entries in the data block
 */
#define VARIABLE(option, var, meta, start, nvars) BYTE(option), LE4(var), \
    LE2((((start) + (nvars)) - __LINE__) * VAR_SIZE + ((meta) - 1) * MD_SIZE)

/* The number of bytes in a metadata format entry */
#define MD_SIZE     5
/*
 * The bytes of a metadata format entry.  The metadata format entries for one
 * data block must appear one per line on consecutive lines, starting on the
 * line after that of the last variable entry for the block.
 * - format is the format code
 * - min is the minimum value
 * - max is the maximum value
 */
#define METADATA(format, min, max) BYTE(code), LE2(min), LE2(max)

#define OPTIONA     0x32
#define OPTIONB     0x33
#define METAFORMAT1 0x2
#define METAFORMAT2 0x7

#define THIS_BLOCK_NVARS 3
/*
 * Hack alert!
 * The source layout of the following declarations is critical.  Do not merge
 * lines or introduce additional lines without understanding what you are doing!
 */
static int decl_start = __LINE__ + 2;
unsigned char data[] = {
    VARIABLE(OPTIONA, varName1, 1, decl_start, THIS_BLOCK_NVARS),
    VARIABLE(OPTIONA, varName2, 1, decl_start, THIS_BLOCK_NVARS),
    VARIABLE(OPTIONB, varName3, 2, decl_start, THIS_BLOCK_NVARS),
    METADATA(METAFORMAT1, 0, 99),
    METADATA(METAFORMAT2, -99, 0)
};

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

Также очень важно: код действителен C++, но он недействителен C99 из-за использования инициализатором данных, которые не являются константой времени компиляции (varname1 и т.д.). Поскольку вы на самом деле компилируете компилятор C++, вы, вероятно, можете это учесть.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню