Я пытаюсь создать портативный API в ANSI C89 / ISO C90 для доступа к беспроводному сетевому устройству через последовательный интерфейс. Библиотека будет иметь несколько сетевых уровней, и различные версии должны работать на встроенных устройствах размером с 8-битный микро с 32 КБ кода и 2 КБ данных, вплоть до встроенных устройств с мегабайтом или более кода и данных.
В большинстве случаев целевой процессор будет иметь единый сетевой интерфейс, и я хочу использовать единую глобальную структуру со всей информацией о состоянии этого устройства. Я не хочу передавать указатель на эту структуру через сетевые уровни.
В некоторых случаях (например, устройство с большим количеством ресурсов, которое должно жить в двух сетях), я буду взаимодействовать с несколькими устройствами, каждое из которых имеет собственное глобальное состояние, и мне нужно будет передать указатель на это состояние (или индекс в массив состояний) через слои.
Я придумал два возможных решения, но ни одно из них не было особенно привлекательным. Имейте в виду, что полный драйвер может содержать 20000 строк или более, охватывать несколько файлов и содержать сотни функций.
Для первого решения требуется макрос, который отбрасывает первый параметр для каждой функции, которой требуется доступ к глобальному состоянию:
// network.h
typedef struct dev_t {
int var;
long othervar;
char name[20];
} dev_t;
#ifdef IF_MULTI
#define foo_function( x, a, b, c) _foo_function( x, a, b, c)
#define bar_function( x) _bar_function( x)
#else
extern dev_t DEV;
#define IFACE (&DEV)
#define foo_function( x, a, b, c) _foo_function( a, b, c)
#define bar_function( x) _bar_function( )
#endif
int bar_function( dev_t *IFACE);
int foo_function( dev_t *IFACE, int a, long b, char *c);
// network.c
#ifndef IF_MULTI
dev_t DEV;
#endif
int bar_function( dev_t *IFACE)
{
memset( IFACE, 0, sizeof *IFACE);
return 0;
}
int foo_function( dev_t *IFACE, int a, long b, char *c)
{
bar_function( IFACE);
IFACE->var = a;
IFACE->othervar = b;
strcpy( IFACE->name, c);
return 0;
}
Второе решение определяет макросы для использования в объявлениях функций:
// network.h
typedef struct dev_t {
int var;
long othervar;
char name[20];
} dev_t;
#ifdef IF_MULTI
#define DEV_PARAM_ONLY dev_t *IFACE
#define DEV_PARAM DEV_PARAM_ONLY,
#else
extern dev_t DEV;
#define IFACE (&DEV)
#define DEV_PARAM_ONLY void
#define DEV_PARAM
#endif
int bar_function( DEV_PARAM_ONLY);
// I don't like the missing comma between DEV_PARAM and arg2...
int foo_function( DEV_PARAM int a, long b, char *c);
// network.c
#ifndef IF_MULTI
dev_t DEV;
#endif
int bar_function( DEV_PARAM_ONLY)
{
memset( IFACE, 0, sizeof *IFACE);
return 0;
}
int foo_function( DEV_PARAM int a, long b, char *c)
{
bar_function( IFACE);
IFACE->var = a;
IFACE->othervar = b;
strcpy( IFACE->name, c);
return 0;
}
Код C для доступа к любому из методов остается прежним:
// multi.c - example of multiple interfaces
#define IF_MULTI
#include "network.h"
dev_t if0, if1;
int main()
{
foo_function( &if0, -1, 3.1415926, "public");
foo_function( &if1, 42, 3.1415926, "private");
return 0;
}
// single.c - example of a single interface
#include "network.h"
int main()
{
foo_function( 11, 1.0, "network");
return 0;
}
Есть ли более чистый метод, который я не понял? Я склоняюсь ко второму, поскольку его легче поддерживать, и становится понятнее, что в параметрах функции есть некоторая макромагия. Кроме того, первый метод требует префикса имен функций с "_", когда я хочу использовать их в качестве указателей на функции.
Я действительно хочу удалить параметр в случае «единого интерфейса», чтобы исключить ненужный код для переноса параметра в стек и позволить функции получить доступ к первому «реальному» параметру в регистре вместо его загрузки. из стека. И, если это вообще возможно, я не хочу поддерживать две отдельные кодовые базы.
Мысли? Идеи? Примеры чего-то похожего в существующем коде?
(Обратите внимание, что использование C ++ не вариант, так как некоторые из запланированных целей не имеют компилятора C ++.)