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

tom*_*gic 5 c api embedded

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

在大多数情况下,目标处理器将具有单个网络接口,并且我将希望使用具有该设备的所有状态信息的单个全局结构.我不想通过网络层传递指向该结构的指针.

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

我提出了两种可能的解决方案,但两者都不是特别漂亮.请记住,完整的驱动程序可能是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;
   }
Run Code Online (Sandbox Code Playgroud)

第二个解决方案定义了在函数声明中使用的宏:

// 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;
   }
Run Code Online (Sandbox Code Playgroud)

访问任一方法的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;
   }
Run Code Online (Sandbox Code Playgroud)

有没有一种我想不通的清洁方法?我倾向于第二,因为它应该更容易维护,并且更清楚的是在函数的参数中存在一些宏观魔力.此外,当我想将它们用作函数指针时,第一种方法需要在函数名前加上"_".

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

思考?想法?现有代码中类似的东西的例子?

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

jmu*_*llo 2

我喜欢你的第二个解决方案。我只是更喜欢将每个函数声明两次,而不是将 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);
Run Code Online (Sandbox Code Playgroud)