C — Универсальная векторная библиотека

Я новичок в C и решил создать общую векторную библиотеку. Вот код:

#ifndef VECTOR_H
#define VECTOR_H

#define DEFAULT_CAPACITY 10
#define EXPAND_RATIO 1.5

#include <stdlib.h>
#include <string.h>

enum error {
    NO_ERROR,
    INDEX_ERROR,
    KEY_ERROR,
    NULL_POINTER_ERROR,
    MEMORY_ERROR
};

#define VECTOR_NEW(vec_name, type)                                           
    /* a resizable array */                                                  
    struct vec_name {                                                        
        size_t length;                                                       
        size_t capacity;                                                     
        type* items;                                                         
    };                                                                       
                                                                             
    /* initializes a vector with a specific capacity */                      
    enum error init_##vec_name##_c(struct vec_name* vec, size_t capacity) {  
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
                                                                             
        vec->length = 0;                                                     
        vec->capacity = capacity;                                            
        vec->items = malloc(capacity * sizeof(*vec->items));                 
                                                                             
        return (vec->items) ? NO_ERROR : MEMORY_ERROR;                       
    }                                                                        
                                                                             
    /* initializes a vector with the default capacity */                     
    static inline enum error init_##vec_name(struct vec_name* vec) {         
        return init_##vec_name##_c(vec, DEFAULT_CAPACITY);                   
    }                                                                        
                                                                             
    /* frees the memory allocated for the vector */                          
    static inline enum error free_##vec_name(struct vec_name* vec) {         
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
        free(vec->items);                                                    
        vec->items = NULL;                                                   
        return NO_ERROR;                                                     
    }                                                                        
                                                                             
    /* adds an item to the end of the vector */                              
    enum error append_##vec_name(struct vec_name* vec, type item) {          
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
        if (vec->length >= vec->capacity) {                                  
            vec->capacity *= EXPAND_RATIO;                                   
            vec->items =                                                     
                realloc(vec->items, vec->capacity * sizeof(vec->items));     
            if (!vec->items)                                                 
                return MEMORY_ERROR;                                         
        }                                                                    
        vec->items[vec->length++] = item;                                    
        return NO_ERROR;                                                     
    }                                                                        
                                                                             
    /* removes and item from the vector at a specified index (-1 as an index 
     * is pop back)                                                          
     */                                                                      
    static inline enum error pop_##vec_name(struct vec_name* vec,            
                                            size_t index, type* item) {      
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
        if (vec->length <= index)                                            
            return INDEX_ERROR;                                              
        if (item)                                                            
            *item = vec->items[index];                                       
        const size_t block_size = (vec->length - index - 1) * sizeof(type);  
        memmove(&(vec->items[index]), &(vec->items[index + 1]), block_size); 
        --vec->length;                                                       
        return NO_ERROR;                                                     
    }                                                                        
                                                                             
    /* sets an existing vector index to a specifid item */                   
    static inline enum error set_##vec_name(struct vec_name* vec,            
                                            size_t index, type item) {       
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
        if (vec->length <= index)                                            
            return INDEX_ERROR;                                              
        vec->items[index] = item;                                            
        return NO_ERROR;                                                     
    }                                                                        
                                                                             
    /* applies the given function pointer to every item in the vector */     
    static inline enum error map_##vec_name(struct vec_name* vec,            
                                            void(func)(type*)) {             
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
        for (size_t i = 0; i < vec->length; ++i)                             
            func(&vec->items[i]);                                            
        return NO_ERROR;                                                     
    }                                                                        
                                                                             
    /* places the item at a specifed index in the`value` parameter */        
    static inline enum error get_##vec_name(struct vec_name* vec,            
                                            size_t index, type* value) {     
        if (!vec)                                                            
            return NULL_POINTER_ERROR;                                       
        if (vec->length <= index)                                            
            return INDEX_ERROR;                                              
        *value = vec->items[index];                                          
        return NO_ERROR;                                                     
    }

#endif  // VECTOR_H

Вот как это используется:

#include "veclib.h"

VECTOR_NEW(vector_int, int)

int main() {
    struct vector_int vec;
    init_vector_int(&vec);

    for (size_t i = 0; i <= 10; ++i) append_vector_int(&vec, i);
}

Я не уверен, элегантен ли мой код, быстр и т. Д. Спасибо!

1 ответ
1

Именование

Очень хорошие комментарии и документация. У меня было четкое представление о том, чего вы пытались достичь.

Вместо того, чтобы давать вашим пользователям возможность создавать любые vec_name, Я бы подумал, например vec_##name, чтобы придать моим векторным типам единообразие именования.

Возможно, что не все ваши функции используются; это обычно приводит к тому, что благонамеренные компиляторы выдают предупреждение для static функции. Я получил этот хороший совет, отключение неиспользуемых статических функций с помощью их.

Распределение

append##vec_name конвертируется в double и назад. Это может сработать для DEFAULT_CAPACITY 10, но распечатка,

printf("append " #vec_name " capacity %lu -> ", vec->capacity); 
vec->capacity *= EXPAND_RATIO; 
printf("%lun", vec->capacity); 

Если это 0 или 1, он застревает в неопределенном поведении,

append vec_foo capacity 1 -> 1

На моем компьютере код работает для одного перераспределения, а затем возникает ошибка сегментации. Легче отлаживать, когда распределение памяти находится в одной функции, а не в обеих init_##vec_name##_c и append_##vec_name. Предложите особенность realloc,

Если ptr является нулевым указателем, realloc () должен быть эквивалентен malloc () для указанного размера.

Функции

В pop##vec_name, в документации указано, что -1 — это задняя часть, что полезно, вроде Python, но я не вижу кода.

В set##vec_name, вы используете задание. Для больших шрифтов это может означать много копирования. Было бы быстрее построить его на месте; это вроде как разница между push_back и emplace_back в C++.

Значение vec является const в set_##vec_name, map_##vec_name, и get_##vec_name. Это может помочь компилятору более агрессивно оптимизироваться и может быть важной самостоятельной документацией: это утверждение, что топология вектора не изменится.

Опечатка: уточняется.

Ошибки

Ваша абстракция ошибок и передача их пользователю великолепны. stdio.h не нужен и даже не входит.

enum error имеет высокую вероятность конфликтов пространства имен с другими заголовками. Возможно, вы захотите использовать возможности, указанные в errno.h вместо того, чтобы изобретать свои собственные. Вместо того, чтобы возвращать одно из нескольких значений, одно возвращает бит (нулевое значение или флаг), предлагая вашим пользователям проверить errno. Это также упростит передачу ошибок из стандартной библиотеки, поскольку вы можете просто передать ошибку вызывающей стороне вместо создания новой ошибки.

Это зависит от того, какой вариант использования для этого, но я бы подумал о том, чтобы сделать ошибки программирования assert вместо динамических проверок (например NULL_POINTER_ERROR.) Я обнаружил, что это освобождает множество случаев от тестирования и проверки.

  • 1

    В set##vec_name, вы используете задание. Это ограничивает векторы присваиваемыми типами. Я бы вообще поставил под сомнение использование этой функции. — Не могли бы вы подробнее рассказать об этом?

    — ксилпекс

  • Я отредактировал это — например а struct или же union не будет назначен. Более общий memset(...sizeof item...) как это было сделано для конструктора, является более общим.

    — Нил


  • На restrict ключевое слово — Будет static inline enum error pop_##vec_name(struct vec_name* restrict vec, size_t index, type* restrict item) быть хорошей идеей (я не использовал restrict перед)?

    — ксилпекс

  • Я думаю, это может помочь, см. использование ограничения, но это влияет на указатели совместимых типов.

    — Нил

  • Я видел это, но является ли мое использование вышеупомянутым хорошей идеей?

    — ксилпекс

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *