Creating a Generic Circular Buffer
Asked Answered
C

1

4

Given the desire to abstract the structure of a circular buffer from its content, and starting from the following code segments (courtesy of this wikipedia entry):

typedef struct
{ 
    int value;
} ElemType;

typedef struct
{
    int         size;      /* total number of elements */
    int         start;     /* index of oldest element */
    int         count;     /* index at which to write new element  */
    ElemType   *elements;  /* vector of elements */
} CircularBuffer;

void cbInit(CircularBuffer *cb, int size) {
    cb->size  = size;
    cb->start = 0;
    cb->count = 0;
    cb->elements = (ElemType *)calloc(cb->size, sizeof(ElemType));
}

How does one abstract the element type so that it is specified when an instance of the CircularBuffer is defined? My attempt thus far is as follows:

CircularBuffer *cbInit(uint16 size, void *element)
{
    CircularBuffer *buffer;

    buffer = malloc(sizeof(*buffer));

    if (buffer != NULL)
    {
        buffer->size = size;
        buffer->start = 0;
        buffer->count = 0;
        buffer->elements = (void *)calloc(size, sizeof(???));

        if (buffer->elements == NULL)
        {
            free(buffer);
            buffer = NULL;
        }
    }

    return buffer;
}

But I cannot figure out how to determine the size of an unknown type, which may be an int, a struct, or anything in between. Is what I am attempting to do even possible?

Choiseul answered 29/11, 2012 at 2:44 Comment(4)
But you will be using pointers to those objects right? Pointers sizes are the same. sizeof(void*)Nogas
This is one of those things that C++ templates makes life so much easier. Here, you either need a raw memory buffer and pass in the size from the user. Or go completely with pointers.Glyco
The code in question is being used to buffer serial UART input on an embedded system. I had initially implemented the buffer using a doubly-linked list, but the overhead imposed by the structure was too great given the actual content of each node is a single char. I am seeking a middle ground where the code is generic enough for me to reuse, without too much overhead in terms of RAM usage.Choiseul
you'd be better of just using a circular buffer of unsgined char (byte) if its for a UART. making it generic will cost you, and you'll be forever casting. The other approach is to macro magic generation of a circular buffer for each type you want.Polyandrist
Z
7

As you've found out, you can't automatically tell the size of an unknown piece of data. You'll need either a fixed element type (void* would be a good generic choice), or have the user pass in the size of each element:

CircularBuffer *cbInit(uint16 size, int elementSize)
{
    ...
    buffer->elementSize = elementSize;
    buffer->elements    = calloc(size, elementSize);  
}
Zapateado answered 29/11, 2012 at 2:53 Comment(2)
The buffer will be storing homogeneous elements - that is, they will all be the same size. It is not true that sizeof(void*) is the size of a pointer to void? If so, what makes it a good generic choice, given it is compiler dependent, and does not correlate to the size of the element itself? Are you suggesting I use an array of pointers, each to a single element?Choiseul
@Choiseul Yes, either an array of pointers, each to a single element; or you can store anything, but the user has to pass in the element size eplicitly. One of those two.Zapateado

© 2022 - 2024 — McMap. All rights reserved.