C API allowing for both automatic and allocated storage

96 Views Asked by At

I'm writing an API that has structs such as

struct datast{
int a;
int *items;
size_t numitems;
};

I'm providing functions which free the contents of such structs (in a similar fashion to what C++ destructors do). Constructors are not provided because I'm mandating zero initialization for them (the .items field is required to be a NULL pointer on initialization, which makes it suitable for later realloc() and free()).

I'm providing however, an additem() function, which realloc()s .items and increases .numitems accordingly.

However, because these structs are small, I'd like to encourage the use of designated initializers and compound literals, so that users can conveniently create these objects with one-liners when possible, without having to manually invoke additem().

But then, if you initialize structs like these with designated initializers (or assign to them from a compound literal), the .items field will have automatic storage rather than allocated storage. And so, if you pass this struct to the "freeing" function/destructor later, you'll be calling free() with an illegal pointer (pointing to automatic storage).

Yes, I know the wording could be "don't call the destructor for objects for which you didn't call additem()"... but this looks really clumsy, and seems like bad design.

Somehow, it's like I had to decide if all these objects should have either automatic or allocated storage, without giving both possibilities to the user.

Have you ever been in a scenario like this? Is there any kind of design I could use that could provide a clean and elegant interface for both automatic and allocated storage?

1

There are 1 best solutions below

2
On BEST ANSWER

Add a boolean member items_allocated. The zero initialisation that you mandate will make that false. Then additem() will set it true:

struct datast
{
    int a;
    int *items;
    bool items_allocated ;
    size_t numitems;
} ;

Then your destructor can have something like:

if( d->items_allocated )
{
    free( d->items ) ;
    d->items = NULL ;
}
d->numitems = 0 ;
...