What would be the preferred C way of implementing a simple generic ring-buffer.
Which approach from the 2 below (or even some 3rd) would you use and why?
Specifically, this will be part of an embedded automotive application, which should conform to MISRA C:2012. I am looking for an approach that is the easiest to understand and maintain. Performance is not a deciding factor.
Option 1, having common structure and using void pointers:
typedef struct ring_buffer
{
    void *start_of_buffer; // inclusive
    void *end_of_buffer; // exclusive
    size_t element_size;
    void *head;
    void *tail;
    size_t count;
} ring_buffer_t;
void * next_pointer(ring_buffer_t *handle, void *pointer)
{
    void *next = (char *)pointer + handle->element_size;
    if (next == handle->end_of_buffer)
        next = handle->start_of_buffer;
    
    return next;
}
void push(ring_buffer_t *handle, void *element)
{
    memcpy(handle->head, element, handle->element_size);
    handle->head = next_pointer(handle, handle->head);
    handle-count++;
}
void pop(ring_buffer_t *handle, void *element)
{
    memcpy(element, handle->tail, handle->element_size);
    handle->tail = next_pointer(handle, handle->tail);
    handle->count--;
}
Option 2, generating type specific structure and using macros:
#define ring_buffer_t(type)    \
    struct                     \
    {                          \
        type *start_of_buffer; \
        type *end_of_buffer;   \
        type *head;            \
        type *tail;            \
        size_t count;          \
    }
#define NEXT_POINTER(handle, pointer) \
    (pointer == (handle.end_of_buffer - 1)) ? start_of_buffer : pointer + 1
#define PUSH(handle, element)                            \
    do                                                   \
    {                                                    \
        *handle.head = element;                          \
        handle.head = NEXT_POINTER(handle, handle.head); \
        handle.count++;                                  \
    } while (0)
#define POP(handle, element)                             \
    do                                                   \
    {                                                    \
        element = *handle.tail;                          \
        handle.tail = NEXT_POINTER(handle, handle.tail); \
        handle.count--;                                  \
    } while (0)
I am leaning more towards option 2 because:
- this is a simple data structure with very simple API and not much logic in push and pop functions
 - option 1 includes some overhead having to store element size and then increment pointers based on this size
 - using clear variable assignment instead of memcpy() as in option 1
 - type-safe pushing and popping as opposed to option 1
 
What I dislike about option 2:
- macros complicating maintainability and possibly making it hard for new developers to understand (although in this case I think it is pretty straightforward)
 - macros making it more difficult to debug (although not much to debug)
 - multiple statements in push and pop macros (although only 3)