stclib / STC

A modern, user friendly, generic, type-safe and fast C99 container library: String, Vector, Sorted and Unordered Map and Set, Deque, Forward List, Smart Pointers, Bitset and Random numbers.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

forward-declaration of containers / working with incomplete type

polijan opened this issue · comments

STC is a great C library! I'd like to request a useful new feature for it.

Could STC provide the option to use containers with incomplete types and forward-declare them?
Whenever the definition of a container type relies on pointers, this ought to be possible. Here's a quick example:

#include <stc/cvec.h>

typedef  struct person  person;

declare_cvec(person, person); // forward declaration
//   ^----.
//        '-- STC could forward declare what relies on pointers:
//
//            typedef struct { person *data;} cvec_person;
//            ...
//
//        .-- this then opens-up new possiblities such as:
//   .----'   
//   v
struct person {
   int          id;
   cvec_person  friends;
};

void   person_del(person* self);
person person_clone(person p);

// now we could complete the definition (with "using_cvec" or another macro)
using_cvec(person, person, c_no_compare, person_del, person_clone);

Let me know if what I mean is clear.

(quick side note [which may or may not be relevant]: I don't know C++ in details, but AFAIK it formerly stated containers were not garantueed to work with incomplete types. However more recent revisions of the C++ standard now specify some STL containers must work with incomplete types)

This is a splendid idea. I don't want to make a full new set of overloaded using-macros, so it requires another parameter to be added to the existing using-macros. If this is placed far back (or last) in the arguments, it will have little or no impact on current usage, but it may be inconvenient when using incomplete types. Depends on how much incomplete types are used.
UPDATED:
Below is how it is currently implemented for cvec, cdeq, and clist. The extra parameter is placed at the very end. I have used c_true and c_false as extra parameters. If this feature is useful, the parameter can be placed earlier, e.g. after _del or _fromraw, although that may break some existing code.

// forward declare cvec with incomplete value types: 
forward_cvec(simple, struct Simple);
...
// finialize 
using_cvec(simple, struct Simple, c_no_compare, c_default_del, c_default_fromraw, c_default_toraw, struct Simple, c_false);

forward_cvec(person, struct Person);
...
// finialize 
using_cvec(person, struct Person, person_compare, person_del, person_clone, c_default_toraw, struct Person, c_false);

// Complete types: NO CHANGE
using_cvec(simple, struct Simple, c_no_compare);
using_cvec(person, struct Person, person_compare, person_del, person_clone);

I keep this as an experimental feature on these three containers as it is, and probably come back to it later and change the position/naming of the using-parameter that specify whether the container was forward-declared.

So, I tested it nesting a cvec "recursively" in a structure. It worked perfectly! The “forward_cvec” is clear and the name is good to give the intent. As for the “using_cvec” bit, yes it's more “verbose” but it's a minuscule price to pay when you need the feature. Very very well done!!! (I hope you'll eventually extend it to most containers: shared pointer, map, ... :)