I've seen few similar post about dynamic array inC released asmacros, 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_HAnd some simple example usage:
#include "dynarray_t.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#define BUFF_SZ 83typedef struct _str_t { char data[BUFF_SZ];} str_t;DARRAY(str_t, readBuff, 101,);int main(void){ int i; self_readBuff = readBuff_t_init(); // init for(i=0; i < 100; i++) { // fill str_t t = {{0}}; snprintf(t.data, sizeof (t.data), "Test line [%d]", i); readBuff_t_add(t, self_readBuff); } int s = self_readBuff->size; for(i=0; i < self_readBuff->size; i++) { // read element at(index) printf("%s\r\n", readBuff_t_getat(i, self_readBuff)->data); } readBuff_t_cleanup(self_readBuff); return 0;}Also please refer toC language only! Not interested in talking forC++, I am quite aware how to work it ontemplate. I need something similar forC, so please give me an advice for design, or spot pitfalls if any.
- \$\begingroup\$Why are you force-appending the
_tto the type? Shouldn't the user decide whether they want areadBuffor areadBuff_t?\$\endgroup\$einpoklum– einpoklum2020-08-02 16:15:55 +00:00CommentedAug 2, 2020 at 16:15 - \$\begingroup\$Suggestion? Good point also.\$\endgroup\$Ilian Zapryanov– Ilian Zapryanov2020-08-02 16:16:35 +00:00CommentedAug 2, 2020 at 16:16
2 Answers2
Use Common Definitions Rather Than Hard Coded Values
I agree with @pm100 aboutNULL, it is much more common to useNULL rather than 0x00. Very early C++ compilers also usedNULL rather thannullptr.
Sincestdlib.h is already included, the exit constantsEXIT_SUCCESS andEXIT_FAILURE are availble, this would make the code more readable and maintainable.
Most modern C and C++ compilers will add a finalreturn 0; to the code so the return inmain() isn't strictly necessary.
Prefersize_t When the Variable Can Be Used As an Index
In main the variablei should be declared assize_t rather thanint. If you compile -Wall you will find that the comparison betweeni andself_readBuff->size yields a type mismatch warning betweenint andsize_t.
In the declaration ofN##t_getat(unsigned int idx, N##_t* _this) theunsigned int should also besize_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.
Inmain() it would be better ifself_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 filestring.h is not necessary and slows down compile time. The variables inmain() 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.
- 1\$\begingroup\$Note that using a signed type instead of
size_twill allow the compiler to optimize more aggressively, but it may also leads bugs. If you use an unsigned type for array/pointer indices, always usesize_t. Also, don't optimize prematurely.\$\endgroup\$yyny– yyny2020-08-02 12:57:11 +00:00CommentedAug 2, 2020 at 12:57 - 1\$\begingroup\$Don't you mean "Onlyinclude what is necessary"?\$\endgroup\$einpoklum– einpoklum2020-08-02 15:25:12 +00:00CommentedAug 2, 2020 at 15:25
- \$\begingroup\$@einpoklum Mostly, but that would leave out the referenced variable
s.\$\endgroup\$2020-08-02 15:30:55 +00:00CommentedAug 2, 2020 at 15:30 - 1\$\begingroup\$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.\$\endgroup\$Ilian Zapryanov– Ilian Zapryanov2020-08-02 17:04:09 +00:00CommentedAug 2, 2020 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.
- \$\begingroup\$Thank you. Will consider it. Feel free to add a snippet\$\endgroup\$Ilian Zapryanov– Ilian Zapryanov2020-08-01 19:35:12 +00:00CommentedAug 1, 2020 at 19:35
You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.


