Ответы пользователя по тегу C++
  • Можно ли через CMake собрать проект, собирающийся bash-скриптом?

    @4rtzel
    add_custom_command полезен когда у вас часть исходников генерируются какой-нибудь другой программой (например flex, protobuf). В таком случае OUTPUT будет указывать на сгенерированные файлы с помощью команды COMMAND. Можете посмотреть мой ответ по теме.

    Самый простой способ (но не всегда самый хороший) собрать сторонний не CMake проект -- использовать ExternalProject_Add. Сложности возникают когда надо добавить этот проект как зависимость к своему. Самый очевидный способ -- это использовать find_package с указанием правильных путей. Например для OpenSSL:
    ...
    # Вместо этой строчки также можно указать путь из командой строки:
    # $ cmake -DOPENSSL_ROOT_DIR=/usr/local/ssl -DOPENSSL_LIBRARIES=/usr/local/ssl/lib ..
    set(CMAKE_MODULE_PATH  ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/openssl)
    # Используем FindOpenSSL.cmake для поиска
    find_package(OpenSSL REQUIRED)
    ...

    Проблема этого способа, что ExternalProject_Add выполняется на этапе сборки (вызов make), а не на этапе конфигурации (вызов cmake), find_package не сработает, если уже нет собранной версии openssl где-то в ${CMAKE_CURRENT_SOURCE_DIR}/openssl.

    Другой вариант добавление зависимости -- использовать комбинацию target_link_directories и target_link_libraries. Пример:
    target_link_directories(my_target ${CMAKE_CURRENT_SOURCE_DIR}/openssl/lib)
    target_link_libraries(my_target PRIVATE crypto ssl)

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

    Во многих проектах такие проблемы решаются, так называемым, SuperBuild'ом. Это когда у вас есть главный CMakeLists.txt файл в которым все остальные проекты (включая ваш) определены как ExternalProject. Тогда можно будет нормально строить зависимости между ними и вызовы типа find_package будут работать, но это уже другая история.
    Ответ написан
    2 комментария
  • Тестированое "ошибками", или как эмулировать их при дебаге и отладке?

    @4rtzel
    Можете посмотреть в сторону fault injection концепции. Она используются как дополнительный шаг в тестировании программы для проверки как ведёт себя код при возникновении ошибок. Минус у всего этого - требуется дополнительная модификация кода (что в вашем случае может быть слишком затратным и избыточным), но существуют вроде и внешние тулзы для модификации кода на лету.

    Как другой вариант - написать скрипт для дебагера, который бы при старте программы расставлял брейкпоинты на retq (если надо, чтобы тело функции выполнилось) инструкции и выполнял кастомный ретурн со ошибочным значением.
    Пример (gdb):
    Файл:
    int my_function() {
      return 42;
    }
     
    int main(void) {
      int a = my_function();
      printf("%d\n", a);
      return 0;
    }


    Находим адрес ret(q):
    (gdb) disassemble my_function
    Dump of assembler code for function my_function:
       0x08048388 <+0>:     push   %ebp
       0x08048389 <+1>:     mov    %esp,%ebp
       0x0804838b <+3>:     mov    $0x2a,%eax
       0x08048390 <+8>:     pop    %ebp
       0x08048391 <+9>:     ret


    В файле .gdbinit:
    b *0x08048391
    commands
    return (int)43
    continue
    end


    Запускаем:
    (gdb) run
    Starting program: /root/a.out
    Breakpoint 1, 0x08048391 in foo ()
    43             <=== новое значение
    [Inferior 1 (process 127) exited normally]
    Ответ написан
    2 комментария
  • Как правильно установить gRPC c++?

    @4rtzel
    Попробуйте:
    git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
    cd grpc
    git submodule update --init
    
    make
    sudo make install # <<< установка grpc
    
    cd third_party/protobuf
    make && sudo make install

    И не забудьте, что pkg-config должен быть установлен до вызова этих команд, а также другие зависимости.

    Можно проверить что всё установилось правильно следующим образом:
    pkg-config --libs grpc
        -L/usr/local/lib -lgrpc
    
    pkg-config --libs protobuf
        -L/usr/local/lib -lprotobuf -pthread -lpthread
    Ответ написан
    Комментировать
  • Как подключить папку с хедерами?

    @4rtzel
    Первое что бросается в глаза - наличие пробелов в пути к папке. Попробуйте взять его в кавычки:
    include_directories("C:\\Program Files\\...\\include") #в этой папке tlhelp32.h

    По поводу правильности:
    Вообще говоря использование include_directories считается моветоном и структура современного CMake должна строиться на targets и properties. В вашем описании немного недостаёт данных, чтобы понять что вы хотите получить. Было бы неплохо если бы вы привели полный код CMake'а и описали что вы пытаетесь получить.

    Или вы также можете глянуть мой другой ответ по построению приложения с CMake'ом.
    Ответ написан
    Комментировать
  • Как правильно организовать модульный проект с использованием CMake?

    @4rtzel
    Поскольку CMake позволяет получить один и тот же результат кучей разный способов, то я постараюсь описать подход, который использовал бы я сам.
    Структура:
    packages_test
    ├── .git
    ├── cmake // Папка с доп. CMake скриптами если в этом есть необходимость
    ├── build // Результат сборки
    │   └─ res.exe // Исполняемый файл
    ├── CMakeLists.txt // Конфигурация сборки проекта
    ├── src
    │   ├─ main.cpp // Точка входа, main()
    │   └─ ...// Прочие файлы проекта
    ├── dependencies // Зависимости (подключаются через механизм submodule)
    │   ├─ vendor_package_0 // У каждой зависимости своя внутренняя организация
    │   └─ vendor_package_1
    └── test // Тесты
        ├── CMakeLists.txt
        └── src
            └── test_main.cpp

    Теперь, как мы будем добавлять зависимости? Это зависит от того где расположены библиотеки и как они собираются. В вашем случае, как я понимаю, все vendor_package_x представляют из себя CMake проекты с использованием add_library и получаемые из git submodule'ей. Это значит, что мы можем использовать add_subdirectory для импорта их target'ов.

    | Note: find_package чаще всего используется для поиска установленных в системе библиотек с помощью FindXXX.cmake скриптов

    Главный CMakeLists.txt:
    cmake_minimum_required(VERSION 3.0)
    project(playrix_project VERSION 1.0 LANGUAGES CXX)
    add_executable(playrix src/main.cpp)
    
    # Импортируем наши зависимости. Это не приводит к сборке, но просто позволяет нам использовать target'ы этих проектов.
    add_subdirectory(dependencies/vendor_package_0)
    add_subdirectory(dependencies/vendor_package_1)
    
    # Забудьте про include_directories и link_directories! В современном CMake следует использовать targets и properties.
    # Опредеяем зависимости нашего проекта
    target_link_libraries(playrix # Имя нашего executable'а
        PRIVATE # Определяет область видимости зависимостей для внешних проектов
            vendor_package_0_target # Настоящее имя target'а надо смотреть в vendor_package_0 CMakeLists.txt (add_library)
            vendor_package_1_target
    )

    target_link_libraries позаботится о том, чтобы собрать все зависимости, слинковать (если надо) их с нашим проектом и инклюднуть необходимые директории. Теперь можно собирать проект!

    Дополнительные ссылки:
    Ответ написан
    2 комментария
  • В каком файле исходников граба2 написано управление клавишами?

    @4rtzel
    Мне кажется, что вы вряд ли найдёте какой-то готовый пример в интернете, но можете попробовать поиграться с этими кусками кода:

    Также я не уверен насчёт вин (супер, мета) клавиши, т.к. не встретил её упоминание в исходниках.
    Ответ написан
  • Как включить в статическую библиотеку все зависимости из других стат.библиотек в 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 комментарий
  • Как сделать простой get запрос используя boost?

    @4rtzel
    Пример с использованием Boost.Beast (доступен с 1.66 версии). За основу взял пример отсюда и слегка изменил его.

    #include <boost/beast.hpp>
    #include <boost/asio/connect.hpp>
    #include <boost/asio/ip/tcp.hpp>
    
    namespace http = boost::beast::http;
    
    int main() {
        const std::string host = "scooterlabs.com";
        const std::string target = "/echo?input=test";
    
        // I/O контекст, необходимый для всех I/O операций
        boost::asio::io_context ioc;
    
        // Resolver для определения endpoint'ов
        boost::asio::ip::tcp::resolver resolver(ioc);
        // Tcp сокет, использующейся для соединения
        boost::asio::ip::tcp::socket socket(ioc);
    
        // Резолвим адрес и устанавливаем соединение
        boost::asio::connect(socket, resolver.resolve(host, "80"));
    
        // Дальше необходимо создать HTTP GET реквест с указанием таргета
        http::request<http::string_body> req(http::verb::get, target, 11);
        // Задаём поля HTTP заголовка
        req.set(http::field::host, host);
        req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
        
        // Отправляем реквест через приконекченный сокет
        http::write(socket, req);
    
        // Часть, отвечающая за чтение респонса
        {
            boost::beast::flat_buffer buffer;
            http::response<http::dynamic_body> res;
            http::read(socket, buffer, res);
    
            std::cout << res << std::endl;
        }
        // Закрываем соединение
        socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    
        return 0;
    }


    Компиляция:
    g++ -lboost_system -pthread main.cpp
    Ответ написан
    7 комментариев