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?

Zack
  • 463
  • 2
  • 7
  • 15
  • But you will be using pointers to those objects right? Pointers sizes are the same. sizeof(void*) – imreal Nov 29 '12 at 02:48
  • 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. – Mysticial Nov 29 '12 at 02:48
  • 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. – Zack Nov 29 '12 at 03:10
  • 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. – Keith Nicholas Nov 29 '12 at 03:39

1 Answers1

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);  
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 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? – Zack Nov 29 '12 at 03:16
  • @Zack 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. – John Kugelman Nov 29 '12 at 14:41