C library

Say I'm creating a library to handle threads in C.

Two state variables are required for tidying up:

static int quux_state; static char* quux_address; /* function to spork quuxes found in a file, reads a line from the file each time it called. */ void spork_quux(FILE*); 

If I store this data as global variables, only one client will be able to start threads at one time, otherwise the state variables will be distorted by the second caller and disaster can occur.

The question is, what is the best way to create a reentrant library in C?

I have considered the following cases so that there is no satisfactory conclusion.

In the following case, the question arises, how to associate the client with each state?

 /* library handles all state data allocation */ static int* quux_state; static char** quux_address; 

In the following case, the client can ruin the state, it is very undesirable

 /* let each client store state */ typedef struct { int state; char* address; } QuuxState; QuuxState spork_quux(FILE*); 

So how to do it right?

+6
c design encapsulation reentrant
source share
3 answers

Use a structure, but do not expose the definition to the client.

i.e. in the .h header file:

 typedef struct QuuxState QuuxState; QuuxState *spork_quux(FILE*); 

and in the implementation file .c:

 struct QuuxState { int state; char* address; }; QuuxState *spork_quuxFILE *f) { QuuxState *quuxState = calloc(1, sizeof(*quuxState)); if (!quuxState) return NULL; quuxState->state = ....; ........ return quuxState; } 

The advantages of this approach are as follows:

  • you can change the contents of the structure without recompiling the entire client code
  • client code cannot access elements, i.e. A quuxState-> state would give a compiler error
  • The QuuxState structure will still be fully visible to the debugger, so you can see the values ​​trivially and set watchpoints, etc.
  • no casting required
  • The type you return is a specific type, so you will get some kind of compiler to check that the correct thing is being passed (compared to the void * pointer).

The only drawback is that you need to allocate a block of memory - however, assuming your library is doing something not trivial (and if it does file I / O, which, of course, is not trivial) the overhead of one malloc is negligible.

You might want to rename the above function to something like "QuuxSpork_create" and add additional functions to work in a "turn-by-turn" mode and destroy the state after completion.

 void QuuxSpork_readLine(QuuxState *state) { .... } void QuuxSpork_destroy(QuuxState *state) { free(state); } 

A random example of a library that works something like this would be the POSIX thread library, pthreads.

+21
source share

Use a struct, but do not tell the client that it is a struct. Issue an opaque pointer - void * or better yet a pointer to an empty dummy structure - and discard it if necessary.

+3
source share

The way to handle most library functions is to return status information to the user in any type of data that they need. In your case, struct. (take strtok vs strtok_r). I believe this creates a use case that you should pass on to the user. Emptiness works. you could even print it so that it looks beautiful.

In addition, strtok_r does this by editing the command line argument without returning a status pointer. I would expect that any re-entry function that I use would adhere to a similar format. Of course, my brain was distorted by some pretty crazy C code.

 void spork_quux(FILE*, QuuxState **); 
+1
source share

All Articles