#Macro Based Dynamic Array in C

65 messages · Page 1 of 1 (latest)

keen isle
#

Quickly made this today. Was planning on using this in my future projects.

keen isle
#

What are some issues with my implementation?

brave heath
#

looks pretty cool

humble grove
# keen isle

there is define_array but not declare_array, so this cannot have external linkage

#

seeing that that's the case, then all the functions should be static

#

and honestly it would make more sense to use external tools for codegen at this point, if basically the entire file has to be a macro

keen isle
#

What tools would you suggest using?

humble grove
#

string replace in basically any language would work well

#

this is unironically a better solution than C macros

#

if you just replaced every instance of <T> with the type of the array, you'd have much more readable code

#

just make CMake or Make generate these files using a two-liner python script and off you go

keen isle
#

thanks I'll look into it

azure wave
#

Using the __auto_type builtin is unnecessary, it can be done in standard C.

keen isle
#

oh?

#

i just switched to array(int)

#

a macro that expands to the header

azure wave
#

Also a reasonable solution

#

Although you also would have to separately declare_list_add(int) and define_list_add(int) every list_add(int)(i) before you use it.

#

Which is doable, but a whole lot of work.

keen isle
#

its not too bad

#
#define define_array(T)    \
define_array_header(T);    \
define_array_alloc(T);     \
define_array_push(T);      \
define_array_pop(T);       \
define_array_free(T);      \
define_array_reserve(T);   \
define_array_clear(T);     \
define_array_erase(T);     \
define_array_insert(T);    \
define_array_shrink(T);    \
define_array_concat(T);    \
define_array_apply(T);     \
define_array_take_back(T); \
define_array_trash(T);     \
define_array_sort(T);      \
#

i do this and its just one line

azure wave
#

Fair enough

keen isle
#

though it will increase binary size

azure wave
#

Unless you pass through an option to make them static

keen isle
#

right

#

static inline

#

or use inline and extern

azure wave
#

inline doesn't really do anything, but yeah

#

I type it because I like typing :)

keen isle
#

i was told inline is a suggestion to the compiler

azure wave
#

A suggestion that most compilers completely ignore

keen isle
#

huh will they still optimize if the function should be inlined

#

like if you dont include inline

azure wave
#

All popular compilers will inline anyway, unless it needs to be exported as a symbol, in which case it may as well just call.

#

Unless the function is small enough that more code would be generated by observing the calling conventions than by inlining.

#

That's roughly the heuristic that's used.

#

Compilers want to output as few instructions as possible.

keen isle
azure wave
#

You make a variable of type T?

#

And you generate the type of the array from macros also

#

You can look at what I posted for how I do it.

#
#define LIST_DEFINE(type)                                          \
  static inline list_##type list_##type##_new() {                  \
    list_##type nl;                                                \
    nl.buf = NULL;                                                 \
    nl.len = 0;                                                    \
    nl.cap = 0;                                                    \
    return nl;                                                     \
  } 
keen isle
#

ah

azure wave
#

Then you can just say list_type outside the macro.

keen isle
#

I have an Array_Header so it would be int_Array_Header maybe i should reconsider the name.

azure wave
#

Yes, why not Array_Header_int

keen isle
#

or just array_int lol

keen isle
#

perferably one that would allow code like this

    array_push_s(p, &k, Package *);
#

where i can just use the types as is without a typedef

#

I guess using code generation tool would allow that among other things

azure wave
#

Nope, just use a typedef

#

For example, typedef char* cstr; LIST_DEFINE(cstr)

#

You can freely assign to/from char*, compiler doesn't care.

keen isle
#

In my implementations I have macro vector(T) that will make anonymous structure with T*, length and capacity and all myself functions takes vector(void*), also I have lot of macros because I need to pass sizeof(*vec->data) to nearly every function

azure wave
#

I had another version where I did:

typedef type* list_type;

// Technically illegal, breaks type-based aliasing analysis,
// but supported by all popular compilers
size_t list_len_type(list_type l) { return *(((size_t*)(char*)l) - 1); }
size_t list_cap_type(list_type l) { return *(((size_t*)(char*)l) - 2); }

list_type list_init_type(size_t cap) {
  char* cptr = (char*)malloc(cap * sizeof(type) + sizeof(size_t) * 2);
  *(((size_t*)cptr) - 1) = 0;
  *(((size_t*)cptr) - 2) = cap;
  return (list_type)cptr;
}

/* Other methods*/

int main() {
  list_type l = list_init_type(5);
  for (size_t i = 0; i < 10; i++)
    list_add_type(l, (type){...});
  for (size_t i = 0; i < list_len_type(l); i++)
    print_type(l[i]);
}

#

The benefit of doing it this way is that you can dereference the list directly.

#

l[i]

#

The downside is that whenever you type .add(), the array may need to be reallocated. Which is bad, because it invalidates all other references to the list.

#

I moved away from this technique, because writing l.buf[i] or l->buf[i] isn't too bad.