Денис Злобин

Урок 16. Полезные инструменты

Привет! Поздравляю, ты дошёл до заключительного урока Части 1 моего курса! На этом уроке я познакомлю тебя с двумя инструментами, которые упрощают жизнь как отдельно взятым разработчикам, так и целым командам.

Изучать работу вспомогательных инструментов мы будем на примере репозитория libmath. Структура репозитория в этом уроке:

libmath/
├── src/                 # Исходные файлы проекта
│   └── libmath.cpp      # Реализация математических функций
├── include/
│   └── libmath.h        # Заголовочный файл математических функций
├── .clang-format        # Конфигурационный файл clang-format
├── .clang-tidy          # Конфигурационный файл clang-tidy
├── .gitignore           # Список файлов, игнорируемых Git
├── CMakeLists.txt       # Конфигурационный файл CMake для сборки проекта
└── README.md            # Описание проекта и инструкции по сборке

Давай разбираться с новыми файлами.

clang-format Я уверен, что любой C++ разработчик хоть раз в своей профессиональной деятельности принимал участие в спорах о стиле кода. Обсуждение таких мелочей, как “на какой строке написать открывающую фигурную скобку” иногда не только отнимает целые часы драгоценного времени разработчика, но и даже приводит к конфликтам с коллегами. Хорошо, что решение проблем, связанных со стилем написания кода, уже давно автоматизировано с помощью утилиты clang-format, которая является монополистом на рынке автоматического форматирования кода для C и C++ проектов.

Для начала установим clang-format:

sudo apt install clang-format-19

Требования к формату кода описываются в конфигурационном файле .clang-format, в котором можно задать любые нюансы формата кода твоего проекта. Честно говоря, я искренне сомневаюсь, что тебе когда-либо придется с нуля создавать конфигурацию стиля кода проекта, но, если с тобой всё таки случится эта неприятность - не унывай, а сразу воспользуйся помощью ИИ - любой ИИ справится с этой задачей на 5+. Либо еще один беспроигрышный вариант: просто используй конфигурационный файл, который я приложу к этому уроку;)

В моём конфигурационном файле clang-format более 100 строчек кода, поэтому прикладывать его к уроку не буду, но ты сможешь найти его в репозитории https://github.com/NeverendingCpp/libmath

Изучим обновленный файл CMakeLists.txt. В файл добавлены новые инструкции для работы с clang-format:

option(ENABLE_CLANG_FORMAT "Enable clang-format" OFF)
if(ENABLE_CLANG_FORMAT)
    find_program(CLANG_FORMAT_PATH NAMES clang-format-19 clang-format REQUIRED)
    message(STATUS "Found clang-format: ${CLANG_FORMAT_PATH}")

    file(GLOB_RECURSE CLANG_FORMAT_SRC CONFIGURE_DEPENDS
        include/*.h
        src/*.cpp
    )

    add_custom_target(clang-format-target
        COMMAND
            ${CLANG_FORMAT_PATH} -style=file -i ${CLANG_FORMAT_SRC}
        WORKING_DIRECTORY
            ${CMAKE_SOURCE_DIR}
        COMMENT
            "Formatting with ${CLANG_FORMAT_PATH} ..."
    )

    add_dependencies(math clang-format-target)
endif()

Разберем построчно.

Добавлен параметр сборки ENABLE_CLANG_FORMAT:

option(ENABLE_CLANG_FORMAT "Enable clang-format" OFF)

По умолчанию параметр отключен, для того, чтобы его активировать необходимо присвоить ему значение ON при сборке проекта. Делается это следующим образом:

cmake -B build -DENABLE_CLANG_FORMAT=ON

При конфигурировании проекта необходимо задать ключ -D, после которого указать имя параметра и через знак равно присвоить значение параметра.

Параметр ENABLE_CLANG_FORMAT используется в условном выражении

if(ENABLE_CLANG_FORMAT)

endif()

В теле условного выражения выполняется поиск установленной утилиты clang-format, при этом сначала ищется утилита 19 версии, а потом всех остальных версий. Если утилита была найдена, путь до исполняемого файла утилиты записывается в переменную CLANG_FORMAT_PATH. С помощью инструкции message в консоль выводится отладочная информация:

find_program(CLANG_FORMAT_PATH NAMES clang-format-19 clang-format REQUIRED)
message(STATUS "Found clang-format: ${CLANG_FORMAT_PATH}")

С помощью следующей инструкции формируется список файлов, к которым будет применено автоформатирование clang-format:

file(GLOB_RECURSE CLANG_FORMAT_SRC CONFIGURE_DEPENDS
	include/*.h
	src/*.cpp
)

С помощью следующей инструкции создается пользовательская цель clang-format-target:

    add_custom_target(clang-format-target
        COMMAND
            ${CLANG_FORMAT_PATH} -style=file -i ${CLANG_FORMAT_SRC}
        WORKING_DIRECTORY
            ${CMAKE_SOURCE_DIR}
        COMMENT
            "Formatting with ${CLANG_FORMAT_PATH} ..."
    )

COMMAND задаёт команду, которая будет выполнена при вызове цели WORKING_DIRECTORY задаёт директорию, в которой будет выполнена команда COMMENT выводит отладочную информацию в консоль

И, наконец, к основной цели math добавляется зависимость от пользовательской цели clang-format-target: при выполнении цели math будет выполнена цель clang-format-target

add_dependencies(math clang-format-target)

Соберем библиотеку с обновленной версией CMake. Сначала выполним конфигурирование.

cmake -B build -DENABLE_CLANG_FORMAT=ON

В случае успешного конфигурирования в выводе в консоль можно найти следующую строчку

Found clang-format: /usr/bin/clang-format-19

После чего выполним сборку проекта:

cmake --build build

В выводе в консоль при сборке можно найти сообщение о применении формата кода:

Formatting with /usr/bin/clang-format-19 ...

В конце можно выполнить команду git status и убедиться, что формат файлов include/libmath.h и src/libmath.cpp был изменен.

clang-tidy Следующий инструмент, с которым ты сегодня познакомишься, это статический анализатор кода clang-tidy. Статический анализатор проверяет код на наличие ошибок во время сборки проекта, т.е. до того, как код будет запущен. Фантастика! Мы можем находить ошибки в коде без ручного или автоматического тестирования.

Установим clang-tidy:

sudo apt install clang-tidy-19

Для поддержки работы с clang-tidy в директорию с проектом необходимо добавить конфигурационный файл .clang-tidy и добавить инструкции в файл CMakeLists.txt.

Начнем с конфигурационного файла. Он достаточно короткий, потому что код математической библиотеки достаточно прост. Но всё таки я добавил одну проверку, благодаря которой clang-tidy покажет нам место для улучшения

Checks: '
-*,
altera-unroll-loops,
'

-* отключает все проверки altera-unroll-loops добавляет проверку циклов, которые могут быть “развернуты” на этапе компиляции. Разворачивание циклов увеличивает быстродействие программы. Более подробно про другие настройки конфигурационного файла можно прочитать здесь: https://clang.llvm.org/extra/clang-tidy/

В отличие от clang-format в CMake добавлена поддержка clang-tidy из коробки

option(ENABLE_CLANG_TIDY "Enable clang-tidy" OFF)
if(ENABLE_CLANG_TIDY)
    find_program(CLANG_TIDY_PATH NAMES clang-tidy-19 clang-tidy REQUIRED)
    message(STATUS "Found clang-tidy: ${CLANG_TIDY_PATH}")

    set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PATH} "-config-file=${CMAKE_SOURCE_DIR}/.clang-tidy")
    set_target_properties(math PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
endif()

Здесь две незнакомые для тебя команды:

set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PATH} "-config-file=${CMAKE_SOURCE_DIR}/.clang-tidy")
set_target_properties(math PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")

Первая команда задает путь до файла с конфигурацией, вторая - добавляет запуск clang-tidy для цели нашего проекта math.

Соберем наш проект вместе с выполнением проверок clang-tidy:

cmake -B build -DENABLE_CLANG_FORMAT=ON -DENABLE_CLANG_TIDY=ON
cmake --build build

В результате получим в консоли следующее замечание от clang-tidy

libmath/src/libmath.cpp:34:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
   34 |     for (int i = 0; i < b; ++i)
      |     ^

clang-tidy указал нам на место кода, в котором можно применить оптимизацию. Я рекомендую тебе самостоятельно внести правки в код, а в этом тебе поможет эта статья https://clang.llvm.org/extra/clang-tidy/checks/altera/unroll-loops.html

На этом последний урок Части 1 подошёл к концу. До встречи в Части 2, которая будет посвящена ООП!