patterncMinor
A small generic array in C
Viewed 0 times
arraygenericsmall
Problem
The generic arrays in C I found on the net used either of these methods:
I wanted it more
I came up with a struct with a typed pointer and heavy preprocesser use, a short example code looks like this:
The full code is here, mostly in ary.h and the rest in ary.c. Below there is the relevant part of the array with the less important functionality (
I'd like to know whether it's bad designed or not useful at all, and if there are errors in the code like UB, bad idioms, etc.
ary.h
``
typedef int (*ary_cm
- The preprocesser with
#defineand#include(a lot of repetition and different names when declaring multiple types, but type-safe).
void *-pointers (no type safety).
void *-pointers and callbacks that fetch specific datatypes from the pointers (troublesome since every datatype needs callbacks and slow due to a lot of callback calls when copying multiple values).
- Typed pointers for direct access and casting them to
voidorcharin general array functions (generic and partly type-safe), actually results in UB since you can't simply alter any pointer to a pointer by dereferencing it asvoidorcharas they may not share the same internal representation.
I wanted it more
template-like, without much preparation, only one name for many types and also without repeating the datatype on every push/pop/etc, still type-safe and also standard compliant (no implementation-defined/undefined behaviour or GNU extensions).I came up with a struct with a typed pointer and heavy preprocesser use, a short example code looks like this:
struct ary(int) a;
ary_init(&a, 0);
ary_push(&a, 30);
ary_push(&a, 20);
ary_push(&a, 10);
ary_sort(&a, ary_cb_cmpint);
ary_reverse(&a);
ary_release(&a);The full code is here, mostly in ary.h and the rest in ary.c. Below there is the relevant part of the array with the less important functionality (
ary_insert, ary_sort, ary_unique, ...) and predefined comp-/stringify-callbacks omitted.I'd like to know whether it's bad designed or not useful at all, and if there are errors in the code like UB, bad idioms, etc.
ary.h
``
#ifndef ARY_H
#define ARY_H
#include
#include
#include
#include
#include
#include
#define ARY_GROWTH_FACTOR 2.0
/ construct/destruct the element pointed to by buf /
typedef void (ary_elemcb_t)(void buf, void *userp);
/ the same as the qsort` comparison function /typedef int (*ary_cm
Solution
Some lesser observations,
-
With heavy use of macros,, care should be applied to insure exactly one use of a macro argument as the macro arguments could be complex and have side effects which only should be evaluated once.
-
Avoid magic numbers like 32. Why 32?
-
As
-
-
Consider simpler code, as below and in other places.
-
Without clear code understanding, I have a bit concern about
-
Uncertain: Comments on # lines is not portable. I'll have to research this.
-
Be careful about
-
With heavy use of macros,, care should be applied to insure exactly one use of a macro argument as the macro arguments could be complex and have side effects which only should be evaluated once.
//#define ary_attach(ary, nbuf, nlen, nalloc) \
// do { \
// ary_freebuf(&(ary)->s); \
// (ary)->s.buf = (ary)->buf = nbuf;\
// ...
#define ary_attach(ary, nbuf, nlen, nalloc) \
do { \
struct aryb *ary__ = ary; // add \
ary_freebuf(&ary__->s); \
ary__->s.buf = ary__->buf = nbuf; \
...-
Avoid magic numbers like 32. Why 32?
// static const size_t snprintf_bufsize = 32;
// Maximum buffer size of string version of `int` log10(bitwidth)
// Other formula are more precise, but better than guessing buffer needs.
static const size_t snprintf_bufsize = sizeof(int)*CHAR_BIT/3 + 3;
int ary_cb_inttostr(char **ret, const void *elem) {
if (!(*ret = ary_xrealloc(NULL, snprintf_bufsize, 1)))
return -1;
return snprintf(*ret, snprintf_bufsize, "%d", *(int *)elem);
}-
As
free(NULL) is OK, IMO, any free-like function should also handle NULL.void ary_freebuf(struct aryb *ary) {
if (ary == NULL) return; // add
if (ary->len && ary->dtor) {-
ary.c should include files like strcmp.h and not count on #include "ary.h" to have included them.-
Consider simpler code, as below and in other places.
// size_t seplen = sep ? strlen(sep) : 0, i, len;
size_t seplen = sep ? strlen(sep) : 0;
size_t i, len;-
Without clear code understanding, I have a bit concern about
strbuf.buf[strbuf.len - 1]. Should strbuf.len == 0, disastrous result would occur.-
Uncertain: Comments on # lines is not portable. I'll have to research this.
#endif /* ARY_H */-
Be careful about
ary_xrealloc(), which calls realloc(). Should ary->len == 0 , return NULL is not an out-of-memorybuf = ary_xrealloc(ary->buf, ary->len, ary->sz);
// if (!buf) return 0;
if (buf == NULL && ary->len > 0 && ary->sz > 0) return 0;Code Snippets
//#define ary_attach(ary, nbuf, nlen, nalloc) \
// do { \
// ary_freebuf(&(ary)->s); \
// (ary)->s.buf = (ary)->buf = nbuf;\
// ...
#define ary_attach(ary, nbuf, nlen, nalloc) \
do { \
struct aryb *ary__ = ary; // add \
ary_freebuf(&ary__->s); \
ary__->s.buf = ary__->buf = nbuf; \
...// static const size_t snprintf_bufsize = 32;
// Maximum buffer size of string version of `int` log10(bitwidth)
// Other formula are more precise, but better than guessing buffer needs.
static const size_t snprintf_bufsize = sizeof(int)*CHAR_BIT/3 + 3;
int ary_cb_inttostr(char **ret, const void *elem) {
if (!(*ret = ary_xrealloc(NULL, snprintf_bufsize, 1)))
return -1;
return snprintf(*ret, snprintf_bufsize, "%d", *(int *)elem);
}void ary_freebuf(struct aryb *ary) {
if (ary == NULL) return; // add
if (ary->len && ary->dtor) {// size_t seplen = sep ? strlen(sep) : 0, i, len;
size_t seplen = sep ? strlen(sep) : 0;
size_t i, len;#endif /* ARY_H */Context
StackExchange Code Review Q#133376, answer score: 2
Revisions (0)
No revisions yet.