Compileable API development to remove the first parameter for most functions and use the global

I am trying to create a portable API in ANSI C89 / ISO C90 to access a wireless network device on a serial interface. The library will have several network levels, and different versions should be run on embedded devices with a size of no more than 8 bits with 32 KB of code and 2 thousand. Data on embedded devices with a megabyte or more of code and data.

In most cases, the target processor will have one network interface, and I want to use one global structure with all the status information for this device. I do not want to pass a pointer to this structure through network layers.

In several cases (for example, a device with a large number of resources that must live in two networks) I will interact with several devices, each with its own global state, and it will need to pass a pointer to this state (or an index to the state array) through layers.

I came up with two possible solutions, but none of them are very good. Keep in mind that a full driver will be 20,000 lines or more, span multiple files and contain hundreds of functions.

The first solution requires a macro that discards the first parameter for each function that should access the global state:

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

The second solution defines macros for use in function declarations:

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

The C code for accessing any method remains the same:

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

, ? , , , - . , "_", .

" ", , , "" , . , , .

? ? - ?

( , ++ , ++.)

+5
3

. , 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);
+2

, , ( - ), , .

DEV, .

:


dev_t *DEV;

int foo_function(int x, int y)
{
    /* DEV->whatever; */
    return DEV->status;
}

int foo_function_multi(dev_t *IFACE, int x, int y)
{
    DEV = IFACE;
    return foo_function(x, y);
}

- args arg ( ) #ifdef MULTI, , arg , , , . , , , ! :


#ifndef MULTI
dev_t *DEV;
#endif
int foo(int x, int y, ...)
{
#ifdef MULTI
    va_list args;
    va_start(args, y);
    dev_t *DEV = va_arg(args, (dev_t*));
    va_end(args);
#endif
    /* DEV->whatever */
    return DEV->status;
}

// call from single
int quux()
{
    int status = foo(23, 17);
}

// call from multi
int quux()
{
    int status = foo(23, 17, &if0);
}

: -)

+2

gcc:

#ifdef TOMSAPI_SMALL
#define TOMSAPI_ARGS( dev, ...) (__VA_ARGS__)
#else  // ! TOMSAPI_SMALL
#define TOMSAPI_ARGS( dev, ...) (dev, ## __VA_ARGS__)
#endif // TOMSAPI_SMALL

#ifdef TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP(local_dev_ptr) device_t * local_dev_ptr = &global_dev; NULL
// The trailing NULL is to make the compiler make you put a ; after calling the macro,
// but without allowing something that would mess up the declaration if you forget the ;
// You can't use the do{...}while(0) trick for a variable declaration.
#else  // ! TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP(local_dev_ptr) device_t * local_dev_ptr = arg_dev; NULL
#endif // TOMSAPI_SMALL

int tomsapi_init TOMSAPI(device_t *arg_dev, void * arg_for_illustration_purposes ) {
    TOMSAPI_DECLARE_DEVP( my_dev );
    my_dev->stuff = arg_for_illustration_purposes;
    return 0;
}

, , API , , . , :

#ifdef TOMSAPI_SMALL
#define TOMSAPI_ARGS(...) (__VA_ARGS__)
#else  // ! TOMSAPI_SMALL
#define TOMSAPI_ARGS(...) (device_t *dev, ## __VA_ARGS__)
#endif // TOMSAPI_SMALL

#ifdef TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP() device_t * dev = &global_dev; NULL 
#else  // ! TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP(local_dev_ptr) NULL
#endif // TOMSAPI_SMALL

int tomsapi_init TOMSAPI(void * arg_for_illustration_purposes ) {
    dev->stuff = arg_for_illustration_purposes;
    return 0;
}

, dev -, .

, , , , , - , , , API ( dev), / , .

EDIT: , , api, , ##, int foo(void) , .

+1
source

All Articles