c - 设计一个带有编译时选项的 API,以删除大多数函数的第一个参数并使用全局参数

标签 c api embedded

我正在尝试在 ANSI C89/ISO C90 中设计一个可移植的 API,以访问串行接口(interface)上​​的无线网络设备。该库将具有多个网络层,并且各种版本需要在嵌入式设备上运行,小到 8 位微处理器,32K 代码和 2K 数据,大到 1 兆字节或更多代码和数据。

在大多数情况下,目标处理器将只有一个网络接口(interface),我想使用一个包含该设备所有状态信息的全局结构。我不想通过网络层传递指向该结构的指针。

在少数情况下(例如,具有更多资源的设备需要在两个网络上运行)我将连接到多个设备,每个设备都有自己的全局状态,并且需要传递指向该状态的指针(或索引)到状态数组)通过层。

我提出了两种可能的解决方案,但都不是特别漂亮。请记住,完整的驱动程序可能有 20,000 行或更多行,涵盖多个文件,并包含数百个函数。

第一个解决方案需要一个宏来丢弃每个需要访问全局状态的函数的第一个参数:

// 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;
   }

有没有我还没想出的更简洁的方法?我倾向于第二个,因为它应该更容易维护,而且更清楚的是函数的参数中有一些宏魔法。另外,当我想将它们用作函数指针时,第一种方法需要在函数名称前加上“_”前缀。

我真的很想删除“单一接口(interface)”情况下的参数,以消除将参数压入堆栈的不必要代码,并允许函数访问寄存器中的第一个“真实”参数,而不是加载它从堆栈。而且,如果可能的话,我不想维护两个独立的代码库。

想法?想法?现有代码中类似的例子?

(请注意,使用 C++ 不是一种选择,因为一些计划的目标没有可用的 C++ 编译器。)

最佳答案

我喜欢你的第二个解决方案。我只是更喜欢将每个函数声明两次,而不是在公共(public) header 中包含该 PARAM 宏。我更喜欢将宏 hijinks 放在隐藏的 C 文件中。

// common header
#ifdef IF_MULTI
    int foo_func1(dev_t* if, int a);
    int foo_func2(dev_t* if, int a, int b);
    int foo_func3(dev_t* if);
#else
    int foo_func1(int a);
    int foo_func2(int a, int b);
    int foo_func3();
#endif

// your C file
#ifdef IF_MULTI
    #define IF_PARM dev_t* if,
    #define GET_IF() (if)
#else
    dev_t global_if;
    #define IF_PARM
    #define GET_IF() (&global_if)
#endif

int foo_func1(IF_PARM int a)
{
    GET_IF()->x = a;
    return GET_IF()->status;
}
int foo_func2(IF_PARM int a, int b)
int foo_func3(IF_PARM);

关于c - 设计一个带有编译时选项的 API,以删除大多数函数的第一个参数并使用全局参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2815238/

相关文章:

c - SPI 在 STM32F103ZE 中读取数据为零

c++ - 如何在 MPI 中创建新类型

我可以使用动态内存分配来减小 int 数组的大小然后重新分配内存吗?

c - 我将浮点值传递给函数 square 但打印的值是 0

php - 通过第 3 方 REST API 进行 Symfony2 身份验证

api - 微服务的集中式 API 文档

c# - 更改 c/c++ dll 中的参数

facebook - 内置类似操作拒绝对象类型

c - 使用 DMA 提高 SPI6 的性能

c - 如何使用TMS3705 RFID应答器基站IC