I've seen few similar post about dynamic array in C released as macros, but I tried a new approach to make it looks more like a template, wrapped in a big macros. However I need a review for suggestions or improvements also. Here is the trivial implementation:

dynarray_t.h

#ifndef DYNARRAY_T_H
#define DYNARRAY_T_H
#include <stdlib.h> /* malloc, calloc, realloc */

//in case no initsize is 0 or less we will assert
#define DARRAY(T, N, INITSIZE, MOD)                                 \
static const char __attribute__((unused))                           \
N##_sassertsizeless[INITSIZE <=0 ? -1 : 1];             \
typedef struct                                                  \
{                                                               \
size_t size, count;                                         \
T* pData;                                                   \
} N##_t;                                                        \
MOD N##_t* self_##N;                                            \
\
static N##_t* N##_t##_init(void)                                \
{                                                               \
N##_t* pN = (N##_t*)malloc(sizeof(N##_t));                  \
if (!pN) return 0x00;                                       \
else {                                                      \
pN->pData = (T*)calloc(INITSIZE, sizeof(T));            \
if (!pN->pData) { free(pN); return 0x00; }              \
else {                                                  \
pN->count = 0;                                      \
pN->size = INITSIZE;                                \
return pN; }                                        \
}                                                       \
}                                                               \
\
static void N##_t##_wiffull(N##_t* _this)                       \
{                                                               \
if (!(_this->count < _this->size-1)) {                      \
T* t = (T*)realloc(_this->pData,                            \
sizeof(T)* _this->size * 2);            \
if (t) {                                                    \
_this->pData = t;                                       \
_this->size *= 2;                                       \
}                                                       \
}                                                           \
}                                                               \
\
static void N##_t##_resizeto(N##_t* _this, size_t ns)           \
{                                                               \
if (ns > _this->size-1) {                                   \
T* t = (T*)realloc(_this->pData,                            \
sizeof(T)* ns * 2);                     \
if (t) {                                                    \
_this->pData = t;                                       \
_this->size = ns * 2;                                   \
}                                                       \
}                                                           \
}                                                               \
\
static void N##_t##_add(T item, N##_t* _this)                   \
{                                                           \
N##_t##_wiffull(_this);                                 \
*(_this->pData+_this->count) = item;                    \
_this->count++;                                         \
}                                                           \
\
static T* N##_t##_getat(unsigned int idx, N##_t* _this)         \
{                                                               \
if (idx < _this->count)                                     \
return &_this->pData[idx];                              \
else return 0x00;                                           \
}                                                               \
\
static void N##_t##_cleanup(N##_t* _this)                       \
{                                                               \
if (_this) {                                                \
if (_this->pData) free(_this->pData);                   \
_this->pData = 0x00;                                    \
free(_this);                                            \
_this = 0x00;                                           \
}                                                           \
}                                                               \
static void N##_t##_add_at(T item, size_t idx, N##_t* _this)    \
{                                                           \
N##_t##_resizeto(_this, idx);                           \
*(_this->pData+idx) = item;                             \
_this->count++;                                         \
}                                                           \

#endif // DYNARRAY_T_H


And some simple example usage:

#include "dynarray_t.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFF_SZ 83
typedef struct _str_t {
char data[BUFF_SZ];
} str_t;

int main(void)
{
int i;

for(i=0; i < 100; i++) { // fill
str_t t = {{0}};
snprintf(t.data, sizeof (t.data), "Test line [%d]", i);
}
}

return  0;
}


Also please refer to C language only! Not interested in talking for C++, I am quite aware how to work it on template. I need something similar for C, so please give me an advice for design, or spot pitfalls if any.

• Why are you force-appending the _t to the type? Shouldn't the user decide whether they want a readBuff or a readBuff_t? Aug 2 '20 at 16:15
• Suggestion? Good point also. Aug 2 '20 at 16:16

## Use Common Definitions Rather Than Hard Coded Values

I agree with @pm100 about NULL, it is much more common to use NULL rather than 0x00. Very early C++ compilers also used NULL rather than nullptr.

Since stdlib.h is already included, the exit constants EXIT_SUCCESS and EXIT_FAILURE are availble, this would make the code more readable and maintainable.

Most modern C and C++ compilers will add a final return 0; to the code so the return in main() isn't strictly necessary.

## Prefer size_t When the Variable Can Be Used As an Index

In main the variable i should be declared as size_t rather than int. If you compile -Wall you will find that the comparison between i and self_readBuff->size yields a type mismatch warning between int and size_t.

In the declaration of N##t_getat(unsigned int idx, N##_t* _this) the unsigned int should also be size_t.

## Prefer Local Variables Over Global Variables

I would suggest a separate macro to define the variable of the proper type so that it can be used in a function rather than having a global variable.

In main() it would be better if self_readBuff was declared locally rather than as a static variable globally. The variable self_##N is not used anywhere else globally.

## Only Code What is Necessary

The header file string.h is not necessary and slows down compile time. The variable s in main() is never referenced. int s = self_readBuff->size;

## Keep it Simple

I would have defined each function as a separate macro and then included all of them in a single macro for ease of debugging and possible separate use. It will also make the code easier to maintain if each function can be maintained separately.

• Note that using a signed type instead of size_t will allow the compiler to optimize more aggressively, but it may also leads bugs. If you use an unsigned type for array/pointer indices, always use size_t. Also, don't optimize prematurely.
– yyny
Aug 2 '20 at 12:57
• Don't you mean "Only include what is necessary"? Aug 2 '20 at 15:25
• @einpoklum Mostly, but that would leave out the referenced variable s`. Aug 2 '20 at 15:30
• Good suggestions. Also I am thinking how convinient would be to make a dynamic array of that dynamic array, guess I will play a bit more taking note of your suggestions. Aug 2 '20 at 17:04

my 2 cents worth

v nice clean code.

I would have called the generated variable N not self_N. That looks peculiar , plus all the other generated names are N## something, having something##N is also odd. In the macro call I said I wanted it called 'readBuff' so call it that.

the use of 0x00 for null is certainly correct buts its the first time I have ever seen it, its not idiomatic. I would say NULL (or plain 0).

Did you consider the possibility of creating the struct on the stack or statically? I mean there is no reason to place it on the heap. It doesn't grow and you don't need variable numbers of them.

• Thank you. Will consider it. Feel free to add a snippet Aug 1 '20 at 19:35