
Implementing a sort of generic, sort of type-safe array in C - ingve
http://kirbyfan64.github.io/posts/implementing-a-sort-of-generic-sort-of-type-safe-arrayin-c.html
======
0x09

       printf("%zu %d\n", list_len(l), *l[0]); // Prints 1 1.
    

This prints raptors unless your list is of size_t.

    
    
       #define list_lenref(l) ((size_t*)l)[-1]
    

The only way to write this kind of size-prefixed plain array in legal C will
involve separate getter/setters to explicitly copy the size in and out. It's
not as simple as getting a reference because you cannot recast an arbitrary
array as a pointer to size_t and read/write to it. A compiler might be happy
to optimize these accesses out altogether.

    
    
       size_t list_len(void* l) {
          size_t tmp;
          memcpy(&tmp, ((char*)l) - sizeof(size_t), sizeof(size_t));
          return tmp;
       }
       void list_set_len(size_t length) {
          memcpy(((char*)l) - sizeof(size_t), &s, sizeof(size_t));
       }
    

Or if you prefer crazy macros

    
    
       #define list_len(l) (*(size_t*)memcpy(&(size_t){0},((char*)l)-sizeof(size_t),sizeof(size_t)))
       #define list_set_len(l,s) memcpy(((char*)l)-sizeof(size_t),&(size_t){s},sizeof(size_t))
    

Note the ((char* )l). Pointer arithmetic on void* as used elsewhere in the
code is a GNU extension. This is easily fixed by instead casting to char* or
u/intptr_t.

Otherwise this style of list is just fine, in fact it's not necessarily
limited to pointer types.

~~~
opk
What are "raptors"?

------
abcd_f
1\. Not using container_of/struct_of, but rather peppering code with "(void*
)l-sizeof(size_t)"

2\. my_list_type.my_array is not properly aligned

3\. list_append() doesn't handle realloc() failures

4\. Doesn't properly () macro argument _and_ lots of macros also reuse their
arguments. That sure is going to be fun to debug, e.g. list_len(l++);

In short, if this works for the author - great, but the re-usability of this
code is next to zero. The beauty of C is that it's very explicit and verbose.
Once you start concealing things behind abstract APIs, you start trading
verbosity for implicit invariants leading to obscure bugs and intense head
scratching. Not a worthy trade off in hell of a lot of cases.

------
opk
As the article states, this only supports lists of pointer types. The result
would be the same bad performance that Bjarne Stroustrup warns of in the cited
talk on linked lists. Each of these pointers will be random memory positions
giving you bad cache hits.

list_append needn't take a second argument that is assigned across. If you've
got an array of structs, you can then initialise the struct members in place.

------
to3m
My growable array "system" is more along these lines:

    
    
        #define CONCAT(A,B) A##B
        #define ALEN_NAME(N) CONCAT(N,_length)
        #define ACAP_NAME(N) CONCAT(N,_capacity)
        #define ARRAY(T,N)       \
            T *N;                \
            size_t ALEN_NAME(N); \
            size_t ACAP_NAME(N)
        #define AINIT(N) do {N=NULL;ALEN(N)=0;ACAP(N)=0;}while(0)
        #define ADESTROY(N) do{free(N);AINIT(N);}while(0)
        #define ALEN(N) (ALEN_NAME(N))
        #define ACAP(N) (ACAP_NAME(N))
        #define AADD(N,V)                                                        \
            do {                                                                 \
                if(ALEN(N)>=ACAP(N)) {N=Grow(&ACAP(N),(N),ALEN(N),sizeof *N,1);} \
                N[ALEN(N)++]=V;                                                  \
            } while(0)
    

(etc., etc.)

I was a bit unsure about it at first, but having used it for a few months I've
ended up quite happy with it:

\- Easy to examine in a debugger;

\- Code is fairly straightforward (taking into account the usual macro token
pasting nonsense);

\- Less risk of falling foul of whatever UB nonsense the gcc/clang people have
decided to pull this week;

\- Simple interop with foreign APIs (of course, reading is very easy; and my
implementation explicitly allows the capacity to be smaller than the size in
order to allow creation of one)

Not very convenient to pass these things to a function, though. I also don't
like that the ARRAY macro generates 3 declarations. These don't really seem to
be problems in practice however.

This might not be to all tastes, but there you go.

(Here's another option, that's more like the link here:
[https://github.com/nothings/stb/blob/master/stretchy_buffer....](https://github.com/nothings/stb/blob/master/stretchy_buffer.h)
\- note note about strict aliasing, which I imagine could apply to the list
macros here as well. Obligatory links: [http://blog.metaobject.com/2014/04/cc-
osmartass.html](http://blog.metaobject.com/2014/04/cc-osmartass.html),
[http://robertoconcerto.blogspot.co.uk/2010/10/strict-
aliasin...](http://robertoconcerto.blogspot.co.uk/2010/10/strict-
aliasing.html))

------
audidude
Link to GArray is wrong, [https://developer.gnome.org/glib/unstable/glib-
Arrays.html](https://developer.gnome.org/glib/unstable/glib-Arrays.html)

------
jevgeni
There's a an error in one of examples:

    
    
        List(int*) mylist = NULL; // NULL represents the empty list.
        int a=1, b=2;
        list_append(l, &a);
    

What's lower case L?

~~~
nrj
It seems that `mylist` should be `l`.

~~~
jevgeni
Yep. The examples on Github use `l` throughout. Just a minor error.

------
Ace17
Why not use ... oh, whatever.

