У меня есть код MCU (для AVR) с набором разных драйверов. Используемый драйвер выбирается во время запуска (в коде инициализации), и одновременно будет использоваться только один из драйверов. Поэтому я могу связать все сегменты ОЗУ в одно и то же место. Это необходимо, так как оперативная память очень ограничена, и всем драйверам требуется 2-3 КБ памяти для буферов и т. д.
В настоящее время у меня есть структуры typedef во всех заголовочных файлах:
typedef struct {
// Driver-specific variables for internal use.
// None of those and accessed from outside C-file
int internal_variable1_for_driver1;
int internal_variable2_for_driver1;
...
} st_driver1_t;
Другие драйверы имеют аналогичную структуру
#include "driver1.h"
#include "driver2.h"
#include "driver3.h"
...
union {
st_driver1_t DRV1;
st_driver2_t DRV2;
st_driver3_t DRV3;
...
} DRV;
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я понимаю, что весь доступ к ОЗУ из модулей, кроме выбранного, должен быть отключен, так как это изменит весь союз. У меня в портфолио много кода ASM, и это очевидно для меня.
Теперь у меня есть некоторые конфликты, потому что я должен включить all_drivers.h и, следовательно, все файлы driverN.h в каждый драйвер. Я хочу скрыть все эти заголовки от всех других драйверов.
Можно ли определить все специфичные для драйвера структуры с помощью __attribute__((common)) в C-файлах и, следовательно, скрыть всю структуру из заголовков. Насколько я понимаю, компоновщик объединит все эти структуры в перекрывающийся сегмент и создаст некий аналог объединения.
struct __attribute__((common)) {
int var1;
int var2;
...
} DRV;
struct __attribute__((common)) {
long OtherVar1;
int ExtraOne;
...
} DRV;
Кроме того, должен ли я определять эти структуры как static или нет? Будет ли это решение стабильным или это недокументированный хак?
«все драйверы требуют 2-3 КБ памяти для буферов и т. д.» Это звучит как очень странный дизайн. Это драйверы ЖК-дисплея или зачем вам столько оперативной памяти?
@Lundin У меня есть универсальное устройство, которое должно работать с большим количеством ведомых устройств, хотя все они используют несовместимые протоколы. Оперативная память используется в основном для буферов Rx/Tx (около 1 КБ каждый). Рабочий режим выбирается с помощью переключателя времени выполнения (из EEPROM)
Какая шина требует таких больших буферов? Ethernet с полноценным TCP/IP?





Можно ли определить все специфичные для драйвера структуры с помощью
__attribute__((common))
С GCC и совместимыми компиляторами вы можете использовать __attribute__((__common__)) для общих переменных (и __attribute__((__no_common__)), если некоторые переменные не должны быть общими, но по умолчанию используются общие).
GCC изменил поведение по умолчанию с -fcommon на -fno-common некоторое время назад (в 2020 году с v10), поэтому вы также можете скомпилировать все единицы компиляции, которые в этом нуждаются, с помощью -fcommon.
должен ли я определить эти структуры как
static[...]?
Нет. Есть несколько условий, которым должны соответствовать объекты, чтобы они были объединены как общие:
Объекты являются глобальными (таким образом, они также находятся в статическом хранилище).
Объекты не имеют инициализаторов.
Объекты имеют одинаковое имя (сборка).
Компилятору следует сообщить, что объекты должны быть общими, как обсуждалось выше.
Иногда желательно, чтобы объекты не имели одинаковых имен на уровне C/C++, например, чтобы объект для driver1 назывался driver1 и т. д. Это нормально, если объекты имеют одинаковые имена на уровне сборки, чего можно добиться с помощью одно и то же имя сборки для всех таких объектов. Это функция, предоставляемая GCC. В следующем примере имя сборки задается как asm_name, поэтому в коде сборки будет использоваться имя asm_name, но в коде C вы используете driver1:
typedef struct { ... } driver1_t;
__attribute__((__common__))
driver1_t driver1 __asm ("asm_name");
Если ABI требует подчеркивания в начале, вы должны использовать имя, которое соответствует этому ABI, и использовать имя сборки, начинающееся с _, например _asm_name.
Есть случаи использования, когда common намного понятнее и удобнее, чем объединение всех объектов в объединение, так что не расстраивайтесь из-за этих комментариев.
Вы не должны использовать ничего из этого, включая странный союз, который выглядит как неправильное решение для реализации либо переключателя компилятора, либо полиморфизма. Забудьте и о нестандартных вещах GNU.
staticобычно достаточно хорош в качестве частной инкапсуляции бедняка для простых драйверов. Если вам нужно что-то более сложное, например несколько экземпляров одного и того же драйвера, обратите внимание на непрозрачные типы: Как сделать приватную инкапсуляцию в C?