It's perfectly possible to do generic vectors in C without twisting the language. This implementation isn't as safe as alternatives in other languages, but plays well on C's strengths.
Generic Containers in C: Vec
61–70 of 91 posts
Re: Generic Containers in C: Vec
#62If you have two dependencies in C which both use the same generic container macros, and both instantiate vec(int) you'll fail-out with redefinition errors.
Re: Generic Containers in C: Vec
#63Many C programmers need proper generic programming mechanisms (perhaps something like Zig's comptime) in C, but macros are the worst possible approach, and they don't want to switch to a different language like C++. As a result, they struggle with these issues. This is what I think the standardization committee should focus on, but instead, they introduced _Generic.
The biggest issue is the ABI for C - it's the lingua-franca of language interoperability and can't really be changed - so whatever approach is taken it needs to be fully compatible with the existing ABI. `_Generic` is certainly flawed but doesn't cause any breaking ABI changes. That's also a major reason why you'd use C rather than C++. The C++ ABI is terrible for language interoperability. It's common for C++ librar…
Then don't use virtual functions. Then there will be no vtables.
You might have known that already, but in general I'm surprised how many engineers think that all C++ classes have vtables. No, most in fact do not. C++ classes generally have the same memory layout as a C struct as long as you don't use virtual functions.
Re: Generic Containers in C: Vec
#64Re: Generic Containers in C: Vec
#65It's amazing how many people try to write generic containers for C, when there is already a perfect solution for that, called C++. It's impossible to write generic type-safe code in C, and this version resorts to using GCC extensions to the language (note the ({…}) expressions). For those afraid of C++: you don't have to use all of it at once, and compilers have been great for the last few decades. You can easily por…
My problem with C++, and maybe this is just me, is RAII. Now, Resource Aquisition Is Initialization is correct, but the corollary is not generally true, which is to say, my variable going out of scope does not generally mean I want to de-aquire that resource. So, sooner or later, everything gets wrapped in a reference counting smart pointer. And reference counting always seemed to me to be a primitive or last-resort…
Same thing as what Rust does with `String` and `str`.
Re: Generic Containers in C: Vec
#66 #define ARRAY(T,S) T S;size_t S##_length;size_t S##_capacity
Then you can make one like this: ARRAY(int,xs);
You'll also need to initialise and these destroy array "objects". #define ARRAY_INIT(S) \
do { \
S=NULL; \
(S##_length)=0; \
(S##_capacity)=0; \
} while(0)
#define ARRAY_DESTROY(S) \
do { \
Array_Free(S); \
ARRAY_INIT(S); \
} while(0)
Add you'll probably want to add an item to an array too. #define ARRAY_ADD(S,X) \
do { \
if((S##_length)>=(S##_capacity)) { \
S=Array_Grow(S, \
sizeof *S, \
&(S##_length), \
&(S##_capacity)); \
} \
S[S##_length++]=(X); \
} while(0)
So you might use them like this: ARRAY(int,xs);
ARRAY_INIT(xs);
for(int i=0;i
Array_Free is very simple, and Array_Grow is barely more complicated (however I wrote it off the cuff, so of course it could still be wrong). Both of these mainly exist just to keep stdlib.h out of the header. void Array_Free(void *p) {
free(p);
}
void *Array_Grow(void *base,size_t stride,size_t *length,size_t *capacity) {
*capacity+=*capacity/2;
*capacity=MAX(*capacity,MAX(MIN_CAPACITY,*length));
return realloc(base,*capacity*stride);
}
Array accesses and iteration and the like are just done in the traditional way. Even performs nicely with -O0. for(size_t i=0;i
You might want to hide the size naming policy behind a macro: #define ARRAY_LENGTH(S) (S##_length)
But it'd often be shorter just to type the name out. (On the other hand, if attempting to fully productize this, maybe it'd be nice to let the consumer customize the naming convention - and now you'd have the option. ARRAY_LENGTH would hide the details, you'd fix up ARRAY_ADD (etc.) to use it, and now you could have the size variable called arraySize (or whatever) and everything would fall into line.)For a full implementation you'll probably also need a way of generating a static array. (I mainly found myself needing this for test code, which uses globals for convenience; most arrays I create normally are locals, or parts of structs.)
You'll also need a parameters list for use in a function declaration or definition, and a macro that expands to all 3 variables.
#define ARRAY_PARAMS(T,S) T *S,size_t S##_length,size_t S##_capacity
#define ARRAY_ARG(S) S,S##_length,S##_capacity
Like then you might have a function that takes a pointer to an "array": void FunctionThatTakesAnArray(ARRAY_PARAMS(T,*p));
And you call it like this: ARRAY(T,myarray);
FunctionThatTakesAnArray(ARRAY_ARG(&myarray));
(I found this cropped up often enough that I needed the macro, but it was less common than I thought.)There's more you can do, but the above is the long and the short of it.
This might all look terrible - or perhaps it sort of looks OK, but you're just not sure that it would actually work - but I've used this in a prototype project and thought it worked out well. (I mainly do C++ nowadays, but I had a good stint of C before that. So even if I've got no taste, hopefully I've at least got a rough feel for what'll work out OK and what'll end up a disaster.)
Re: Generic Containers in C: Vec
#67Many C programmers need proper generic programming mechanisms (perhaps something like Zig's comptime) in C, but macros are the worst possible approach, and they don't want to switch to a different language like C++. As a result, they struggle with these issues. This is what I think the standardization committee should focus on, but instead, they introduced _Generic.
Re: Generic Containers in C: Vec
#68From my experience, trying to make C type safe is counter productive. It's perfectly possible to do generic vectors in C without twisting the language. This implementation isn't as safe as alternatives in other languages, but plays well on C's strengths. https://github.com/codr7/hacktical-c/tree/main/vector
Re: Generic Containers in C: Vec
#69Earlier quoted context omitted.
> ... we would need a standardized name-mangling scheme, ... may you please elaborate on _why_ you think this is needed ?
If the templates are monomorphized, each instantiation of a templated function will have a different address. To acquire the address of any given instantiation we need a symbol in the object file.
Re: Generic Containers in C: Vec
#70An important property of C++ templates is that their instantiations are de-duplicated at link-time. If you have two dependencies in C which both use the same generic container macros, and both instantiate vec(int) you'll fail-out with redefinition errors.