2

I'd like to write a library in C and I don't know what is the recommended way. I got for example structure and multiple functions like this:

typedef struct example
{
   int *val;
   struct example *next; 
} Example;

and I have build function for multiple types of val

Example* build() { do sth };
Example* buildf() { do sth }; // val is float
Example* buildd() { do sth }; // val is double

What is the better practice (used in "professional" library). Use pointer to void and casting or have structure for all possibilities - int, float, double.

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
Uncle Jone
  • 51
  • 3

2 Answers2

2

Use a union and some way to store type info:

typedef struct example
{
   enum{ T_STRUCT_WITH_INT, T_STRUCT_WITH_FLOAT, T_SO_ON } type;
   union {
      int val_int;
      float val_float;
   } val;
   struct example *next; 
} Example;

Access fields after checking type by s->val.val_int

In C11 you can have union anonymous and fields can be accessed like s->val_int

Vasfed
  • 18,013
  • 10
  • 47
  • 53
  • If possible types are limited - have pointers inside struct, this is a bit type-safer than just casting a void and the same in terms of memory and performance – Vasfed Jan 16 '16 at 23:12
2

This is primarily based on some combination of opinion, experience and the specific requirements at hand.

The following approach is possible, inspired by some container library work by Jacob Navia. I've never used it myself:

struct container_node {
   struct container_node *link_here, *link_there, *link_elsewhere;
   /*...*/
   char data[0]; /* C90 style of "flexible array member" */
};

struct container_node *container_node_alloc(size_t data_size);

The allocation function allocates the node large enough so that data[0] through data[data_size-1] bytes of storage are available. Through another set of API functions, user data of arbitrary type be copied in and out.

The following approach is sometimes called "intrusive container". The container defines only a "base class" consisting of the link structure. The user must embed this structure into their own structure:

struct container_node {
   struct container_node *next, *prev;
};

void container_insert(struct container *container, struct container_node *n);

struct container_node *container_first(struct container *container);

The user does this:

struct my_widget {
   struct container_node container_links;
   int widget_height;
   /* ... */
};

/* .... */

/* We don't insert my_widget, but rather its links base. */
container_insert(&widg_container, &widget->container_links);

Some macros are used to convert between a pointer to the widget and a pointer to the container links. See the container_of macro used widely in the Linux kernel:

struct my_widget *wptr = container_of(container_first(&widg_container),
                                      struct my_widget, container_links);

See this question.

Then there approaches of storing a union in each node, which provides an integer, floating-point-value or a pointer. In that case, the data is separately allocated (though not necessarily: if the caller controls the allocation of the nodes, it's still possible to put the node structure and the user data in a buffer that came from a single malloc call).

Finally, there are also approaches which wrap these techniques with preprocessor templating, an example of which are the BSD QUEUE macros.

Community
  • 1
  • 1
Kaz
  • 55,781
  • 9
  • 100
  • 149