Ответы пользователя по тегу C
  • Работа с различными типами данных в си?

    @4rtzel
    Существует множество способов реализации полиморфной коллекции на С и у каждой реализации есть свои недостатки и достоинства. Самый популярный и простой из них, наверно, через void*. Хотя он и подходит для большинства случаев он всё же имеет недостаток в виде непоследовательного размещения данных в памяти и типобезопасности. Так что вот ещё парочка возможных способов реализации, которые могут подойти вам лучше:

    Запихнув всю реализацию массива в макрос можно объявлять необходимые, типобезопасные массивы при необходимости.
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    #define ARRAY_DECLARE(type) \
    struct array_##type {       \
        size_t size;            \
        size_t capacity;        \
        type* data;             \
    };                          \
    \
    struct array_##type* array_##type##_create(size_t init_cap) {       \
        struct array_##type* self = malloc(sizeof(struct array_##type));\
        self->capacity = init_cap;                                      \
        self->data = calloc(init_cap, sizeof(type));                    \
    }                                                                   \
    \
    void array_##type##_push_back(struct array_##type* self, type value) {  \
        if (self->size == self->capacity) {                                 \
            type* temp = calloc(self->size + 1, sizeof(type));              \
            memmove(temp, self->data, self->size * sizeof(type));           \
            free(self->data);                                               \
            self->data = temp;                                              \
        }                                                                   \
        self->data[self->size++] = value;                                   \
    }
    
    ARRAY_DECLARE(int);
    ARRAY_DECLARE(float);
    
    int main(void) {
        struct array_int* iarr = array_int_create(1);
        array_int_push_back(iarr, 42);
        array_int_push_back(iarr, 1337);
        for (int i = 0; i < 2; ++i) {
            printf("%d\n", iarr->data[i]);
        }
    
        struct array_float* farr = array_float_create(1);
        ...
        return 0;
    }


    Через "наследование".
    /* Где-то в array.h */
    struct array {
        size_t size;
        size_t capacity;
        void* data;
    
        void (*push_back)(struct array* self, void* value);
        void* (*get)(struct array* sefl, size_t index);
    };
    
    /* Где-то в arra_int.h */
    void array_int_push_back(struct array* self, void* value) {
        if (self->size == self->capacity) {
            int* temp = calloc(self->size + 1, sizeof(int));
            memmove(temp, self->data, self->size * sizeof(int));
            free(self->data);
            self->data = temp;
        }
        int* int_data = (int*)self->data;
        int_data[self->size++] = *(int*)value;
    }
    void* array_int_get(struct array* self, size_t index) {
        return ((int*)self->data) + index;
    }
    struct array* array_int_create(size_t init_cap) {
        struct array* self = malloc(sizeof(struct array));
        self->push_back = array_int_push_back;
        self->get = array_int_get;
        self->capacity = init_cap;
        self->data = calloc(init_cap, sizeof(int));
        return self;
    }
    
    int main(void) {
        struct array* arr = array_int_create(1);
        int value = 42;
        arr->push_back(arr, &value);
        value = 1337;
        arr->push_back(arr, &value);
        for (int i = 0; i < 2; ++i) {
            printf("%d\n", *((int*)arr->get(arr, i)));
        }
        return 0;
    }


    Ещё можно обратить внимение на новый _Generic keyword.
    Весь код выше приведён только в целях демонстрации и, безусловно, содежрит баги :)

    Также советую посмотреть как реализованы такие структуры в крупных проектах:
    1. Glib
    2. Linux
    3. Postgres
    4. Другие ссылки в интернете коих полно.

    Надеюсь это вам поможет выбрать подходящую реалзизацию для вашей задачи.
    Ответ написан
    Комментировать
  • Как включить в статическую библиотеку все зависимости из других стат.библиотек в CMake проекте?

    @4rtzel
    Хотя я бы конечно советовал использовать find_package/add_subdirectory для подключение этой либы к программе, но если всё же стоит задача получить static либу, то можно попробовать что-то вроде этого (на основе этого ответа https://stackoverflow.com/a/37941544/9953527):

    CMakeLists.txt:
    cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
    project(mylib_combined VERSION 0.1 LANGUAGES CXX)
    
    # Нам нужны статические либы
    set(Boost_USE_STATIC_LIBS ON) 
    find_package(Boost COMPONENTS system filesystem REQUIRED) 
    
    # Наша собственная либа.
    # К сожалению мы не может использовать add_library OBJECT либу, т.к.
    # CMake не умеет работать с IMPORTED OBJECT либами, т.е. он не умеет
    # экстрактить (ar -x) существующею static либу, чтобы собрать её потом 
    # в одну большую (ar -qcs).
    add_library(mylib mylib.cpp) 
    add_custom_command(
        TARGET mylib
        POST_BUILD
        # Экстрактим нашу свежесобранную либу. Не самое красивое решение, но
        # что поделать ¯\_(ツ)_/¯
        COMMAND ar -x $<TARGET_FILE:mylib>
        # Теперь наши буст либы
        COMMAND ar -x ${Boost_SYSTEM_LIBRARY}
        COMMAND ar -x ${Boost_FILESYSTEM_LIBRARY}
        # А теперь собираем всё обратно
        COMMAND ar -qcs lib${PROJECT_NAME}.a *.o
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )


    Для работы с этой библиотекой с другого CMake проекта понадобиться add_library IMPORTED таргет. Но если вы всё-таки планируете использовать её в других CMake проектов, то лучше сделать что-то вроде этого:

    CMakeLists.txt:
    cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
    project(mylib_combined VERSION 0.1 LANGUAGES CXX)
    
    set(Boost_USE_STATIC_LIBS ON) 
    find_package(Boost COMPONENTS system filesystem REQUIRED) 
    
    add_library(mylib mylib.cpp) 
    
    add_custom_target(mylib_packer
        COMMAND ar -x $<TARGET_FILE:mylib>
        COMMAND ar -x ${Boost_SYSTEM_LIBRARY}
        COMMAND ar -x ${Boost_FILESYSTEM_LIBRARY} 
        COMMAND ar -qcs lib${PROJECT_NAME}.a *.o
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        DEPENDS mylib
    )
    
    add_library(mylib_combined STATIC IMPORTED GLOBAL)
    add_dependencies(mylib_combined mylib_packer)
    set_target_properties(mylib_combined
        PROPERTIES
            IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib${PROJECT_NAME}.a
    )


    И можно использовать mylib_combined таргет.

    Очевидно, код выше будет работать только на unix системах. Для Windows/Mac OS есть свои тулзы, но тут я ничего подсказать не могу. Могу только посоветовать вынести всю работы с ar/libtool/lib в отдельный .cmake файл для дальнейшего переиспользования и подключать его с помщью include.

    Полезные ссылки:
    https://gitlab.kitware.com/cmake/community/wikis/d...
    https://pabloariasal.github.io/2018/02/19/its-time...
    https://cmake.org/pipermail/cmake/2014-August/0582...
    https://cmake.org/gitweb?p=cmake.git;a=blob;f=Test...
    Ответ написан
    1 комментарий