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, ... :)