По просьбе beastie выкладываю хладный рассказ,
beastie
переведёшь стрелки на меня ;)
_________________________________________________
Предыстория
Дысклаймер: История произошла более чем полгода назад, поэтому технические подробности могу помнить не до конца
Существовало две разрабатываемые железки, назовем их А и Б. Разрабатывались в одном КБ и даже в одном отделе. Железка А делала свою часть работы по сканированию, а железка Б получала данные по кабелю, толщине которого позавидуют тетки из BRAZZERS. Стоит сказать, что прошивки обеих машинок были написаны на си. Ритм всему действию задавала машинка А. Ее «папаня» писал хэдэр с объявленной структурой примерно так:
typedef struct
{
int32_t FFT1;
int32_t FFT2;
char Bflags:3;
char Aflags:13;
} IrrMainType
И все было хорошо, на машинке Б существовала долговременная память, в которую при компиляции вгонялся бинарь для парсинга входных данных от А. Компилилось один раз, потом можно было уже этот блок не трогать, а спокойно заливать куда-то другие модули, компилять их отдельно и спокойно работать. Стоит сказать, что механизм компиляции придумали интересный - существовал USB разъем, к которому подключалась флешка, а в прошивке сделали команду «recompile <modulename>», так что в случае чего можно было флешку заменить устройством выхода в сеть (локальную или глобальную) и удаленно залить прошивку и попросить текущую работающую версию перекомпилиться и ребутнуться. Стоит отметить, что прошивка основного модуля рекомпилилась всегда на старте, беря сорцы со все той же флешки. Сделано это было для того, чтобы быть уверенным что в устройстве последняя версия.
P.S. Для тех кто не понял как - над всем этим работал сильнопорезанный линукс, который бутился, делал свои дела в виде компиляций и инициализаций, а потом запускал нужный бинарь.
_________________________________________________
Проблема
Дысклаймер: проблема была реальной, по крайней мере эту часть я помню на 100%
Устройствами занимались два «папани». Причем хорошо, когда они сообщали друг другу об изменениях. Основной проблемой было то, что программист А без предупреждения менял структуру передачи данных. Другими словами, структуры описанная выше с мановения руки становилась такой:
typedef struct
{
int32_t FFT1;
int32_t FFT2;
int32_t rechargetime;
char Bflags:3;
char Aflags:13;
} IrrMainType
_________________________________________________
Решение
Дысклаймер: предлагаю до чтения этой части самостоятельно подумать о решении, все данные даны.
Мое решение оказалось таким: давайте использовать макросы, чтобы генерировать и структуру и числовой хеш, запомнив который в блоке постоянной памяти можно было бы сравнивать с хешем из нового хэдера и если они различаются - перекомпилировать блок. Иными словами:
[ Блок постоянной памяти ]
+-- Линукс
+-- Бинарь прошивки обработки входных данных
+-- Бинарь основной прошивки
+-- Текстовый файл с хешем
#define IRR_STRUCT(STRUCT) \
typedef struct { STRUCT } IrrMainType; \
int32_t const structhash = strhash(#STRUCT);
IRR_STRUCT
(
int32_t FFT1;
int32_t FFT2;
int32_t rechargetime;
char Bflags:3;
char Aflags:13;
)
...
int32_t oldhash = readhashFromFile;
if (oldhash != structhash)
sendCommand("recompile datamodule");
writeHashToFile(structhash);
// Пробелы и переносы расставлены для наглядности.
typedef struct
{
int32_t FFT1;
int32_t FFT2;
int32_t rechargetime;
char Bflags:3;
char Aflags:13;
} IrrMainType;
int32_t const structhash
= strhash("int32_t FFT1; int32_t FFT2; "
"int32_t rechargetime; char B"
"flags:3; char Aflags:13;");
И таки знаете что? Работало без нареканий.