Home > Back-end >  Using circular list with variable datatypes in c
Using circular list with variable datatypes in c

Time:09-30

I searched the net for over two days to make a circular list that could be used for different data types. I use c language, so I mean I am looking for some code that can manage buffers with char, short, int, signed, or unsigned definitions. what I have found more useful is these 2 links

Embedded artistry

StackOverflow

The problem I have is that I am working on embedded boards with an external ram and since I do have not enough memory inside the processor chip, I have to use external memory, and in this case, malloc function would not work correctly.

So I am looking for some code or enhancing the embedded artistry code to be flexible to different data types.

What I had done with no good result was to change this code into two parts. one for char type and one for short type.

struct circular_buf_t {
    uint8_t *buffer;
    size_t head;
    size_t tail;
    size_t max; // of the buffer
    bool full;
};

struct circular_buf_short_t {
    void *buffer;
    size_t head;
    size_t tail;
    size_t max; // of the buffer
    bool full;
};

cbuf_handle_t circular_buf_init(uint8_t *buffer, size_t size) {
    cbuf_handle_t cbuf = malloc(sizeof(circular_buf_t));
    cbuf->buffer = buffer;
    cbuf->max = size;
    circular_buf_reset(cbuf);
    return cbuf;
}

cbuf_handle_short_t circular_buf_short_init(uint16_t *buffer, size_t size) {
    cbuf_handle_short_t cbuf = malloc(sizeof(cbuf_handle_short_t));
    circular_buf_short_reset(cbuf);
    cbuf->buffer = buffer;
    cbuf->max = size;
    return cbuf;
}

int circular_buf_try_put(cbuf_handle_t me, uint8_t data) {
    int r = -1;
    if (!circular_buf_full(me)) {
        me->buffer[me->head] = data;
        advance_head_pointer(me);
        r = 0;
    }
    return r;
}

int circular_buf_short_try_put(cbuf_handle_short_t me, uint16_t data) {
    int r = -1;
    if (!circular_buf_short_full(me)) {
        me->buffer[me->head] = data;
        advance_head_pointer_short(me);
        r = 0;
    }
    return r;
}

CodePudding user response:

I have/use many versions of the following doubly linked list code. It uses macros to do templating.

This is the most compact/standalone version.

I created it as a part of a previous SO answer: Implementing input/output redirection in a Linux shell using C

The templating macros are DLH* and DLK*

// shpipe/shpipe.h -- shell pipe control

#ifndef _shpipe_shpipe_h_
#define _shpipe_shpipe_h_

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>

#define SUPER_INLINE        __attribute__((always_inline)) static inline

#ifdef _SHPIPE_GLO_
#define EXTRN_SHPIPE        /**/
#else
#define EXTRN_SHPIPE        extern
#endif

typedef unsigned char byte;
typedef unsigned int u32;

#define CAST(_typ,_val) \
    ((_typ) (_val))

#define FWD(_typ) \
    struct _##_typ; \
    typedef struct _##_typ _typ##_t; \
    typedef _typ##_t *_typ##_p

FWD(xstr);
FWD(xpipe);
FWD(redir);
FWD(xcmd);
FWD(xlst);

#define SHANYMAX            300         // maximum for anything

// bit mask operations
#define BTVOFF(_bitno)          ((_bitno) >> 3)
#define BTVMSK(_bitno)          (1u << ((_bitno) % 0x07))
#define BTVLEN(_bitcnt)         ((((_bitcnt)   7) >> 3) << 3)
#define BTVSET(_vec,_bitno)     _vec[BTVOFF(_bitno)] |= BTVMSK(_bitno)
#define BTVCLR(_vec,_bitno)     _vec[BTVOFF(_bitno)] &= ~BTVMSK(_bitno)

#ifndef DLHCNT
#define DLHCNT              1
#endif

#ifndef XMEMCHK
#define XMEMCHK             1
#endif

#if XMEMCHK
#define syscalloc(_cnt,_len,_mag)   _syscalloc(_cnt,_len,_mag,__FILE__,__LINE__)
#define sysfree(_vp)                _sysfree(_vp)
#define sysmchk()                   _sysmchk()
#else
#define syscalloc(_cnt,_len,_mag)   calloc(_cnt,_len)
#define sysfree(_vp)                free(_vp)
#define sysmchk()                   /**/
#endif

#ifndef XFDCHK
#define XFDCHK              1
#endif

#define XFDMAX              1024

#if XFDCHK
#define xfdget(_vec)                _xfdget(_vec)
#define xfdchk(_vec)                _xfdchk(_vec)
#else
#define xfdget(_vec)                /**/
#define xfdchk(_vec)                /**/
#endif

pid_t pidtop;                           // top/master process id
FILE *xfintop;                          // top input stream

EXTRN_SHPIPE byte xfdchk_std[BTVLEN(XFDMAX)];

// magic numbers
typedef enum {
    SYSMAGIC_XSTR = 0xDEADBE01,
    SYSMAGIC_XCMD = 0xDEADBE02,
    SYSMAGIC_XLST = 0xDEADBE03,
    SYSMAGIC_ARGV = 0xDEADBE04,
} sysmagic_t;

// allocation control
FWD(xmem);
struct _xmem {
    sysmagic_t xmem_magic;              // magic number
    xmem_p xmem_next;                   // linkage
    const char *xmem_file;              // file
    int xmem_lno;                       // line number
    //long xmem_rsvp;
};
EXTRN_SHPIPE xmem_p xmemall;

EXTRN_SHPIPE int opt_debug;

#define sysfault(_fmt...) \
    do { \
        fprintf(stderr,_fmt); \
        exit(1); \
    } while (0)

#define dbgprt(_lvl,_fmt...) \
    do { \
        if (opt_debug) \
            _dbgprt(_fmt); \
    } while (0)

#define _dbgprt(_fmt...) \
    fprintf(stderr,_fmt)

#define dbgexec(_lvl,_expr) \
    do { \
        if (opt_debug) \
            _expr; \
    } while (0)

#define OPTALL(_cmd) \
    _cmd(TOKSEP,"special separater") \
    _cmd(TOKQUO1,"single quoted string") \
    _cmd(TOKQUO2,"double quoted string") \
    _cmd(TOKWORD,"ordinary word") \
    _cmd(OPTNOGO,"error occurred") \
    _cmd(OPTDETACH,"detach job") \
    _cmd(OPTLOCK,"struct locked") \
    _cmd(OPTIPIPE,"input pipe") \
    _cmd(OPTOPIPE,"output pipe") \
    _cmd(OPTDONE,"command done")

#define _OPTDEF(_sym,_reason) \
    _##_sym,
enum {
    OPTALL(_OPTDEF)
};

#define _OPTMSK(_sym,_reason) \
    _sym = (1 << _##_sym),
enum {
    OPTALL(_OPTMSK)
};

// option/symbol display control
FWD(tgb);
struct _tgb {
    const char *tgb_tag;                // symbol name
    u32 tgb_val;                        // symbol value
    const char *tgb_reason;             // symbol explanation
};

#define TGBEOT \
    { .tgb_tag = NULL }
#define TGBMORE(_tgb) \
    _tgb != NULL

#define _OPTTGB(_sym,_reason) \
    { .tgb_tag = #_sym, .tgb_val = _sym, .tgb_reason = _reason },
#ifdef _SHPIPE_GLO_
tgb_t opt_tgb[] = {
    OPTALL(_OPTTGB)
    TGBEOT
};
#else
extern tgb_t opt_tgb[];
#endif

FWD(dlh);
FWD(dlk);
FWD(dlv);

#define DLKDEF(_typ) \
    sysmagic_t _typ##_magic;            /* magic number */ \
    u32 _typ##_opt;                     /* options */ \
    _typ##_p _typ##_prev;               /* pointer to previous item in list */ \
    _typ##_p _typ##_next                /* pointer to next item in list */

#define DLKOF(_ptr) \
    CAST(dlk_p,_ptr)

#define DLHDEF(_pre,_typ) \
    _typ##_p _pre##head;                /* head of list */ \
    _typ##_p _pre##tail;                /* tail of list */ \
    dlv_p _pre##dlv;                    /* virtual function table pointer */ \
    int _pre##cnt;                      /* number of items in list */

#define DLHOF(_ptr,_pre) \
    CAST(dlh_p,&_ptr->_pre##head)

// doubly linked list virtual function table
struct _dlv {
    void (*dlv_free)(dlk_p dlk);        // free the item
    void (*dlv_show)(dlk_p dlk,const char *reason);     // show the item
};

// doubly linked list item
struct _dlk {
    DLKDEF(dlk);
};

// doubly linked list header
struct _dlh {
    DLHDEF(dlh_,dlk);
};

// string buffer
struct _xstr {
    DLKDEF(xstr);                       // linkage
    u32 xstr_type;                      // string type

    size_t xstr_maxlen;                 // maximum space in string buffer
    char *xstr_lhs;                     // pointer to start of string
    char *xstr_rhs;                     // pointer to current string append

    char *xstr_file;                    // input file
    long xstr_fpos;                     // input file position
    int xstr_lno;                       // input file line number

    char *xstr_bufptr;                  // pointer to buffer
    long xstr_bufoff;                   // beginning buffer offset
};

// pipe control
struct _xpipe {
    int xpipe_fd[2];                    // pipe units
};
#define XPIPE_READ          0           // read/input side of pipe
#define XPIPE_WRITE         1           // write/output side of pipe

// redirection control
struct _redir {
    xstr_p red_sep;                     // separater
    xstr_p red_file;                    // filename
    int red_fd;                         // open file unit
};

// close file descriptor
#define CLOSEME(_fd) \
    _fd = closeme(_fd,__FILE__,__LINE__)

#define XSTRFREE(_xstr) \
    _xstr = xstrfree(_xstr)

// command control
struct _xcmd {
    DLKDEF(xcmd);                       // linkage
    DLHDEF(xcmd_,xstr);                 // list of strings

    redir_t xcmd_redir[2];              // redirection control
    xpipe_t xcmd_xpipe;             // output side pipe

    pid_t xcmd_pid;                     // process id
    int xcmd_status;                    // completion status
};

#define XCMD_DLHOF(_xcmd) \
    DLHOF(_xcmd,xcmd_)

// command list
struct _xlst {
    DLKDEF(xlst);                       // linkage
    DLHDEF(xlst_,xcmd);                 // list of commands
};

#define XLST_DLHOF(_xlst) \
    DLHOF(_xlst,xlst_)

EXTRN_SHPIPE xstr_t parseinfo;          // information
EXTRN_SHPIPE char *parsebuf;            // current buffer
EXTRN_SHPIPE char *parsecur;            // current offset
EXTRN_SHPIPE int errstop;               // errors

#include <shpipe.proto>

// closeme -- close a unit
SUPER_INLINE int
closeme(int fd,const char *file,int lno)
{

#if XFDCHK
    fd = _closeme(fd,file,lno);
#else
    do {
        if (fd < 0)
            break;
        dbgprt(1,"closeme: CLOSE fd=%d file='%s' lno=%d\n",
            fd,file,lno);
        close(fd);
        _fd = -1;
    } while (0)
#endif

    return fd;
}

// btvget -- fetch bit vector value
SUPER_INLINE byte
btvget(const byte *vec,int bitno)
{

    vec  = BTVROFF(bitno);

    return *vec & BTVMSK(bitno);
}

// btvset -- set bit vector value
SUPER_INLINE void
btvset(byte *vec,int bitno,int val)
{
    byte msk;

    vec  = BTVROFF(bitno);
    msk = BTVMSK(bitno);

    if (val)
        *vec |= msk;
    else
        *vec &= ~msk;
}

// dlhinc -- increment/decrement list count
SUPER_INLINE void
dlhinc(dlh_p dlh,int inc)
{
#if DLHCNT
    dlh->dlh_cnt  = inc;
#endif
}

// xstrcstr -- get the "c string" value
SUPER_INLINE char *
xstrcstr(xstr_p xstr)
{

    return xstr->xstr_lhs;
}

// xcmd virtual function table
#ifdef _SHPIPE_GLO_
dlv_t xcmd_dlv = {
    .dlv_free = xstrfree_dlv,
    .dlv_show = xstrshow_dlv,
};
#else
extern dlv_t xcmd_dlv;
#endif

// xlst virtual function table
#ifdef _SHPIPE_GLO_
dlv_t xlst_dlv = {
    .dlv_free = xcmdfree_dlv,
    .dlv_show = xcmdshow_dlv,
};
#else
extern dlv_t xlst_dlv;
#endif

#endif
// shpipe/dlk.c -- "smart" list "class" for C

#include <shpipe.h>

// dlhfree -- free list
dlh_p
dlhfree(dlh_p dlh)
{

    _dlhfree(dlh);
    sysfree(dlh);

    dlh = NULL;

    return dlh;
}

// _dlhfree -- clean list
dlh_p
_dlhfree(dlh_p dlh)
{
    dlv_p dlv;
    dlk_p dlk;
    dlk_p next;

    dlv = dlh->dlh_dlv;

    for (dlk = dlh->dlh_head;  dlk != NULL;  dlk = next) {
        next = dlk->dlk_next;
        dlv->dlv_free(dlk);
    }

    dlh = NULL;

    return dlh;
}

// dlkpush -- append string to end of list
dlk_p
dlkpush(dlh_p dlh,dlk_p dlk)
{
    dlk_p tail;

    tail = dlh->dlh_tail;

    dlk->dlk_next = NULL;
    dlk->dlk_prev = tail;

    if (tail != NULL)
        tail->dlk_next = dlk;

    dlh->dlh_tail = dlk;

    if (dlh->dlh_head == NULL)
        dlh->dlh_head = dlk;

    dlhinc(dlh,1);

    return dlk;
}

// dlhcut -- cut out entry and split list
void
dlhcut(dlh_p dlhlhs,dlh_p dlhrhs,dlk_p dlk)
{
    dlk_p prev;
    dlk_p next;

    dbgprt(1,"dlhcut: ENTER\n");

    dbgexec(1,dlhrhs->dlh_dlv->dlv_show(dlk,"dlhcut"));

    dbgexec(1,dlhshow(dlhrhs,"dlhrhs/BEF"));

    prev = dlk->dlk_prev;
    next = dlk->dlk_next;

    // set left side list
    dlhlhs->dlh_tail = prev;
    if (dlk == dlhrhs->dlh_head)
        dlhlhs->dlh_head = NULL;
    else
        dlhlhs->dlh_head = dlhrhs->dlh_head;

    // set right side list
    dlhrhs->dlh_head = next;
    if (dlk == dlhrhs->dlh_tail)
        dlhlhs->dlh_tail = NULL;

    // break sibling links
    if (next != NULL)
        next->dlk_prev = NULL;
    if (prev != NULL)
        prev->dlk_next = NULL;

    // recount the nodes in both lists
#if DLHCNT
    dlhlhs->dlh_cnt = 0;
    dlhinc(dlhrhs,-1);
    for (dlk_p cur = dlhlhs->dlh_head;  cur != NULL;  cur = cur->dlk_next) {
        dlhinc(dlhlhs,1);
        dlhinc(dlhrhs,-1);
    }
#endif

    dlk->dlk_prev = NULL;
    dlk->dlk_next = NULL;

    dbgexec(1,dlhshow(dlhrhs,"dlhrhs/AFT"));
    dbgexec(1,dlhshow(dlhlhs,"dlhlhs/AFT"));

    dbgprt(1,"dlhcut: EXIT\n");
}

// dlkunlink -- remove item from list
dlk_p
dlkunlink(dlh_p dlh,dlk_p dlk)
{
    dlk_p prev;
    dlk_p next;

    prev = dlk->dlk_prev;
    next = dlk->dlk_next;

    dlhinc(dlh,-1);

    if (prev != NULL)
        prev->dlk_next = next;
    else
        dlh->dlh_head = next;

    if (next != NULL)
        next->dlk_prev = prev;
    else
        dlh->dlh_tail = prev;

    dlk->dlk_prev = NULL;
    dlk->dlk_next = NULL;

    return dlk;
}

// dlhcnt -- calculate item count
int
dlhcnt(dlh_p dlh)
{
    int cnt;

#if DLHCNT
    cnt = dlh->dlh_cnt;
#else
    cnt = 0;
    for (dlk_p dlk = dlh->dlh_head;  dlk != NULL;  dlk = dlk->dlk_next)
        cnt  = 1;
#endif

    return cnt;
}

// dlhshow -- output a list
void
dlhshow(dlh_p dlh,const char *reason)
{
    dlk_p dlk;
    dlv_p dlv;

    _dbgprt("dlhshow: ENTER (from %s)\n",reason);

    dlv = dlh->dlh_dlv;

    for (dlk = dlh->dlh_head;  dlk != NULL;  dlk = dlk->dlk_next) {
        _dbgprt("dlhshow: OPT dlk_opt=%s\n",strtgb(opt_tgb,dlk->dlk_opt));
        dlv->dlv_show(dlk,reason);
    }

    _dbgprt("dlhshow: EXIT\n");
}

As mentioned in my other answer, the full code for the shell is too large to fit in an answer, so it's here: http://pastebin.com/Ny1w6pUh

CodePudding user response:

For your purpose, you could use a generic circular buffer structure that you initialize with an array of the type needed (eg: 8, 16 or 32 bit integers) along with the element size and element count.

The ancillary functions work the same for all types, and the get and put functions handle the specific type with a test.

If you know the type of element for a given buffer, you can use the specific get/put function.

Here is a sample implementation:

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct circular_buf_t {
    void *buffer;
    size_t head;  // you can make these uint16_t to save memory
    size_t tail;
    size_t element_count;   // number of elements in the buffer
    uint8_t element_size;
    bool full;
} circular_buf_t;

circular_buf_t *circular_buf_reset(circular_buf_t *cbuf) {
    cbuf->head = cbuf->tail = 0;
    cbuf->full = false;
    return cbuf;
}

circular_buf_t *circular_buf_init(void *buffer, size_t element_size, size_t element_count) {
    if (element_size == 1 || element_size == 2 || element_size == 4) {
        circular_buf_t *cbuf = malloc(sizeof(*cbuf));
        cbuf->buffer = buffer;
        cbuf->element_count = element_count;
        cbuf->element_size = (uint8_t)element_size;
        return circular_buf_reset(cbuf);
    } else {
        return NULL;
    }
}

bool circular_buf_full(circular_buf_t *me) {
    return me->full;
}

bool circular_buf_empty(circular_buf_t *me) {
    return me->head == me->tail && !me->full;
}

int circular_buf_advance_head_pointer(circular_buf_t *me) {
    if (  me->head == me->element_count)
        me->head = 0;
    me->full = (me->head == me->tail);
    return 0;
}

int circular_buf_advance_tail_pointer(circular_buf_t *me) {
    if (  me->tail == me->element_count)
        me->tail = 0;
    me->full = 0;
    return 0;
}

int circular_buf_try_put(circular_buf_t *me, int32_t data) {
    if (circular_buf_full(me)) {
        return -1;
    } else {
        switch (me->element_size) {
        case 1: ((uint8_t  *)me->buffer)[me->head] = (uint8_t)data; break;
        case 2: ((uint16_t *)me->buffer)[me->head] = (uint16_t)data; break;
        case 4: ((int32_t  *)me->buffer)[me->head] = data; break;
        }
        return circular_buf_advance_head_pointer(me);
    }
}

int circular_buf_try_put8(circular_buf_t *me, uint8_t data) {
    if (circular_buf_full(me) || me->element_size != sizeof(data)) {
        return -1;
    } else {
        ((uint8_t *)me->buffer)[me->head] = data;
        return circular_buf_advance_head_pointer(me);
    }
}

int circular_buf_try_put16(circular_buf_t *me, uint16_t data) {
    if (circular_buf_full(me) || me->element_size != sizeof(data)) {
        return -1;
    } else {
        ((uint16_t *)me->buffer)[me->head] = data;
        return circular_buf_advance_head_pointer(me);
    }
}

int circular_buf_try_put32(circular_buf_t *me, int32_t data) {
    if (circular_buf_full(me) || me->element_size != sizeof(data)) {
        return -1;
    } else {
        ((int32_t *)me->buffer)[me->head] = data;
        return circular_buf_advance_head_pointer(me);
    }
}

int32_t circular_buf_get(circular_buf_t *me) {
    int32_t res = -1;
    if (!circular_buf_empty(me)) {
        switch (me->element_size) {
        case 1: res = ((uint8_t  *)me->buffer)[me->head]; break;
        case 2: res = ((uint16_t *)me->buffer)[me->head]; break;
        case 4: res = ((int32_t  *)me->buffer)[me->head]; break;
        }
        circular_buf_advance_tail_pointer(me);
    }
    return res;
}

int32_t circular_buf_try_get8(circular_buf_t *me) {
    int32_t res = -1;
    if (!circular_buf_empty(me) && me->element_size == 1) {
        res = ((uint8_t *)me->buffer)[me->tail];
        circular_buf_advance_tail_pointer(me);
    }
    return res;
}

int32_t circular_buf_try_get16(circular_buf_t *me) {
    int32_t res = -1;
    if (!circular_buf_empty(me) && me->element_size == 2) {
        res = ((uint16_t *)me->buffer)[me->tail];
        circular_buf_advance_tail_pointer(me);
    }
    return res;
}

int32_t circular_buf_try_get32(circular_buf_t *me) {
    int32_t res = -1;
    if (!circular_buf_empty(me) && me->element_size == 4) {
        res = ((int32_t *)me->buffer)[me->tail];
        circular_buf_advance_tail_pointer(me);
    }
    return res;
}

CodePudding user response:

I am looking for some code that can manage buffers with char, short, int, signed, or unsigned definitions.

There are two main possibilities:

  • prepare a single generic implementation that handles all those data types (and more); or
  • write a separate implementation specific to each data type you want to handle.

Additionally, although you do need to provide memory for the list to use, by no means does that have to be dynamically allocated memory.

Generic approach

The generic implementation would need to carry the size of the element type as a property of each list, it would need to use memcpy / memmove instead of assignment, and if you need operations such as comparisons of elements then they would need to be supported by appropriate type-specific supplementary functions. The standard library's qsort() function can give you a general idea of the kinds of interfaces that would be involved.

This variation on the code presented in the question might look like so:

struct circular_buf_t {
    void *buffer;
    size_t element_size;
    size_t head;
    size_t next;
    size_t capacity;
    _Bool full;
};

// Returns the struct by value
struct circular_buf_t circular_buf_init(void *buffer, size_t element_size, size_t num_elements) {
    struct circular_buf_t buf = { .buffer = buffer, .element_size = element_size, .capacity = num_elements };
    // members not explicitly initialized are default-initialized

    return buf;
}

// add an element at the end of the buffer
int circular_buf_try_put(struct circular_buf_t *me, void *data) {
    if (me->full) {
        return -1;
    } else {
        // This is a convenience for indexing calculations, but there are other
        // ways to do it if your implementation does not support VLAs:
        char (*elements)[me->element_size] = me->buffer;

        memmove(elements[me->next], data, me->element_size);
        me->next = (me->next   1) % me->capacity;
        if (me->next == me->head) {
            me->full = 1;
        }
    }
}

short buffer_storage[BUFFER_ELEMENTS];

void example(void) {
    struct circular_buf_t short_buffer = circular_buf_init(buffer_storage, sizeof(buffer_storage[0]), BUFFER_ELEMENTS);
    short datum = 42;
    int result = circular_buf_try_put(&short_buffer, &datum);
}

A disadvantage of this approach is that you cannot add values directly to the buffer. Values to be added must be stored in memory, in objects of the correct type.

Type-specific implementations approach

For its part, the separate implementation angle could be assisted by macros to avoid any actual source code duplication -- this would be the C analog of using templates in C . This might perform better, but your binary will be (a bit) larger if it uses lists with multiple element types. Example:

#define CIRCULAR_BUF_TYPE(e_type) struct circular_ ## type ## _buf_t

#define CIRCULAR_BUF_IMPL(e_type) \
CIRCULAR_BUF_TYPE(e_type) { \
    e_type *buffer; \
    size_t head; \
    size_t next; \
    size_t capacity; \
    _Bool full; \
}; \
\
CIRCULAR_BUF_TYPE(e_type) circular_ ## e_type ## _buf_init(e_type *buffer, size_t num_elements) { \
    CIRCULAR_BUF_TYPE(e_type) buf = { .buffer = buffer, .capacity = num_elements }; \
    return buf; \
} \
int circular_ ## e_type ## _buf_try_put(CIRCULAR_BUF_TYPE(e_type) *me, e_type data) { \
    if (me->full) { \
        return -1; \
    } else { \
        me->buffer[next] = data; \
        me->next = (me->next   1) % me->capacity; \
        if (me->next == me->head) { \
            me->full = 1; \
        } \
    } \
} \
extern int buf_dummy

// Example
CIRCULAR_BUFFER_IMPL(short);

short buffer_storage[BUFFER_ELEMENTS];

void example(void) {
    struct circular_short_buf_t buffer = circular_short_buf_init(buffer_storage, BUFFER_ELEMENTS);
    int result = circular_short_buf_try_put(&buffer, 42);
}

You could, of course, factor that into more macros, and that might be to your advantage for debugging. Also, that will have trouble with element types expressed using multiple words, but that shortcoming could be resolved with a bit more effort.

Generic interface

If you want to provide implementations for several specific element types known in advance, then you could augment the type-specific variation with generic interfaces. For example,

CIRCULAR_BUFFER_IMPL(char);
CIRCULAR_BUFFER_IMPL(short);
CIRCULAR_BUFFER_IMPL(int);  // 'int' is the same as 'signed'
CIRCULAR_BUFFER_IMPL(unsigned);

#define circular_buf_init(storage, elements) _Generic(storage, \
  char *: circular_char_buf_init(storage, elements), \
  short *: circular_short_buf_init(storage, elements), \
  int *: circular_int_buf_init(storage, elements), \
  unsigned *: circular_unsigned_buf_init(storage, elements) \
)

#define circular_buf_try_put(buf, item) _Generic(buf, \
  struct circular_char_buf_t *: circular_char_buf_try_put(buf, item), \
  struct circular_short_buf_t *: circular_short_buf_try_put(buf, item), \
  struct circular_int_buf_t *: circular_int_buf_try_put(buf, item), \
  struct circular_unsigned_buf_t *: circular_unsigned_buf_try_put(buf, item) \
)

// Example
CIRCULAR_BUFFER_IMPL(short);

short sbuffer_storage[BUFFER_ELEMENTS];
int   ibuffer_storage[BUFFER_ELEMENTS];

void example2(void) {
    struct circular_short_buf_t short_buffer = circular_buf_init(sbuffer_storage, BUFFER_ELEMENTS);
    struct circular_int_buf_t int_buffer = circular_buf_init(ibuffer_storage, BUFFER_ELEMENTS);

    int result = circular_buf_try_put(&short_buffer, 42);
    result = circular_buf_try_put(&int_buffer, INT_MAX);
}
  • Related