Создание универсального циклического буфера

Учитывая желание абстрагировать структуру циклического буфера от его содержимого и начиная со следующих сегментов кода (любезно предоставлено эта запись в Википедии):

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));
}

Как абстрагировать тип элемента, чтобы он указывался при определении экземпляра CircularBuffer? Моя попытка до сих пор заключается в следующем:

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;
}

Но я не могу понять, как определить размер неизвестного типа, который может быть int, struct или чем-то средним. Возможно ли то, что я пытаюсь сделать?


person Zack    schedule 29.11.2012    source источник
comment
Но вы будете использовать указатели на эти объекты, верно? Размеры указателей одинаковые. размер (пусто*)   -  person imreal    schedule 29.11.2012
comment
Это одна из тех вещей, которые шаблоны C++ значительно упрощают жизнь. Здесь вам либо нужен необработанный буфер памяти, либо вы передаете размер от пользователя. Или перейти полностью с указателями.   -  person Mysticial    schedule 29.11.2012
comment
Рассматриваемый код используется для буферизации последовательного ввода UART во встроенной системе. Первоначально я реализовал буфер, используя двусвязный список, но накладные расходы, связанные со структурой, были слишком велики, учитывая, что фактическое содержимое каждого узла представляет собой один символ. Я ищу золотую середину, где код достаточно общий для повторного использования без особых накладных расходов с точки зрения использования ОЗУ.   -  person Zack    schedule 29.11.2012
comment
вам лучше просто использовать круговой буфер unsgined char (byte), если это для UART. сделать его общим будет стоить вам, и вы будете вечно кастовать. Другой подход заключается в генерировании магией макросов циклического буфера для каждого типа, который вы хотите.   -  person Keith Nicholas    schedule 29.11.2012


Ответы (1)


Как вы уже поняли, вы не можете автоматически определить размер неизвестного фрагмента данных. Вам понадобится либо фиксированный тип элемента (void* будет хорошим общим выбором), либо пользователь должен передать размер каждого элемента:

CircularBuffer *cbInit(uint16 size, int elementSize)
{
    ...
    buffer->elementSize = elementSize;
    buffer->elements    = calloc(size, elementSize);  
}
person John Kugelman    schedule 29.11.2012
comment
Буфер будет хранить однородные элементы, то есть все они будут одного размера. Неверно, что sizeof(void*) - это размер указателя на void? Если да, то что делает его хорошим общим выбором, учитывая, что он зависит от компилятора и не коррелирует с размером самого элемента? Вы предлагаете мне использовать массив указателей, каждый на один элемент? - person Zack; 29.11.2012
comment
@Zack Да, либо массив указателей, каждый на отдельный элемент; или вы можете сохранить что угодно, но пользователь должен явно указать размер элемента. Один из тех двоих. - person John Kugelman; 29.11.2012