7
\$\begingroup\$

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;


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 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.

\$\endgroup\$
  • \$\begingroup\$ Why are you force-appending the _t to the type? Shouldn't the user decide whether they want a readBuff or a readBuff_t? \$\endgroup\$ – einpoklum Aug 2 at 16:15
  • \$\begingroup\$ Suggestion? Good point also. \$\endgroup\$ – Ilian Zapryanov Aug 2 at 16:16
7
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ – yyny Aug 2 at 12:57
  • 1
    \$\begingroup\$ Don't you mean "Only include what is necessary"? \$\endgroup\$ – einpoklum Aug 2 at 15:25
  • \$\begingroup\$ @einpoklum Mostly, but that would leave out the referenced variable s. \$\endgroup\$ – pacmaninbw Aug 2 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 Aug 2 at 17:04
3
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thank you. Will consider it. Feel free to add a snippet \$\endgroup\$ – Ilian Zapryanov Aug 1 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.