First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro
newdoes not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to havenew(Vector(int))callnew_Vector_int():#define new(type) \ PASTE(new_, type)()The second solution is to have
new(Vector(int))resolve tonew_Vector_intand add the call by hand on the calling site. This would allow to "overload constructors":#define new(type) \ PASTE(new_, type) Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);The
define_vector_##Tmacro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be astaticvariable in a function. Not that this is a good idea either, but you get it...).The
typedefbetweenstruct _vector_##TandVector_##Tis done quite late. I could have used the "forwardtypedef":typedef struct _vector_##T Vector_##T;This would have allowed me use
Vector_##Tinstead ofstruct _vector_##Tin the function declarations.Actually, I can also use
Vector(T)directly instead ofVector_##Tin the body ofdefine_Vector. Thanks to that, I can drop many##operators and the code ends up being easier to read.The macros parameters are not always correctly guarded with parenthesis. If I have a stack-allocated vector and try to give its address to a function (e.g.
begin(&vec_i)), it fails miserably. More parenthesis are needed:#define begin(collection) \ (collection)->_functions->begin(collection)Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-
constreferences). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example offront. The declaration becomes:T* vector_front_##T(const Vector(T)*);The definition becomes:
T* vector_front_##T(const Vector(T)* vector) { return vector->_data; }And the macro
frontbecomes:#define front(collection) \ (*((collection)->_functions->front(collection)))This new trick allows us to get rid of the workaround macros
elem,firstandlastsinceat,frontandbackcan now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.The
define_vector_##Tmacro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be astaticvariable in a function. Not that this is a good idea either, but you get it...).