Заготовка репозитория на CMake с тестами GoogleTest и настроенными проверками качества кода. Работает одинаково на Linux, macOS и Windows.
Все задачи курса живут в одном репозитории, каждая - отдельной папкой в tasks/.
Настраивать сборку заново для каждой задачи не нужно.
| Система | Компилятор | CMake |
|---|---|---|
| Windows | Visual Studio 2022 Community, компонент "Разработка классических приложений на C++" | идёт с Visual Studio, либо отдельно |
| Linux | sudo apt install build-essential |
sudo apt install cmake |
| macOS | xcode-select --install |
brew install cmake |
Проверка: cmake --version должна показать 3.21 или новее.
Из корня репозитория. Папку build создавать руками не нужно.
cmake --preset dev # настроить (один раз)
cmake --build --preset dev # собрать
ctest --preset dev # прогнать тестыДолжно собраться и пройти 2 теста из папки tasks/example.
Запуск программы:
./build/dev/tasks/example/example # Linux / macOS
.\build\dev\tasks\example\Debug\example.exe # WindowsVisual Studio, CLion и VS Code (расширение CMake Tools) читают
CMakePresets.jsonсами: откройте папку репозитория, и пресеты появятся в списке конфигураций.
- Скопируйте папку
tasks/exampleвtasks/<имя задачи>, напримерtasks/task1b. - Переименуйте файлы и пространство имён под свою задачу.
- Поправьте
tasks/<имя>/CMakeLists.txt:
oop_task(
NAME task1b # имя задачи, оно же имя исполняемого файла
SOURCES word_count.cpp # файлы реализации (без main.cpp и без тестов)
MAIN main.cpp # файл с функцией main, можно не указывать
TESTS word_count_test.cpp # файлы тестов, можно не указывать
)- Выполните
cmake --preset devзаново - новая папка подхватится автоматически.
Заголовочные .h-файлы в CMakeLists.txt перечислять не нужно: они не образуют отдельных
единиц трансляции. .cpp-файлы - нужно.
Итоговая структура:
tasks/
task1a/ CMakeLists.txt, module1.h/.cpp, module2.h/.cpp, main.cpp
task1b/ CMakeLists.txt, word_count.h/.cpp, main.cpp, word_count_test.cpp
task2/ CMakeLists.txt, bit_array.h/.cpp, bit_array_test.cpp
...
Когда задач в репозитории несколько, пересобирать и прогонять всё каждый раз незачем.
oop_task(NAME task1b ...) заводит цели с предсказуемыми именами:
| Цель | Что это |
|---|---|
task1b |
Исполняемая программа (есть, если задан MAIN) |
task1b_tests |
Тесты задачи (есть, если задан TESTS) |
task1b_lib |
Код задачи, только компиляция без линковки |
task1b_bench |
Замеры (только в пресете bench) |
Собрать и запустить одну задачу:
cmake --build --preset dev --target task1b # собрать только её
./build/dev/tasks/task1b/task1b # запустить (Linux / macOS)
.\build\dev\tasks\task1b\Debug\task1b.exe # запустить (Windows)Прогнать тесты только одной задачи - имена тестов начинаются с её имени:
cmake --build --preset dev --target task1b_tests
ctest --preset dev -R '^task1b\.'Запустить один конкретный тест или группу - напрямую через тестовый файл, так виден подробный вывод и можно фильтровать по шаблону:
./build/dev/tasks/task1b/task1b_tests # все тесты задачи
./build/dev/tasks/task1b/task1b_tests --gtest_list_tests # какие вообще есть
./build/dev/tasks/task1b/task1b_tests --gtest_filter='*Empty*' # только подходящиеПосмотреть список всех целей в проекте:
cmake --build --preset dev --target helpПроверки стиля и форматирования (tidy, format, format-check) работают сразу
по всем задачам - разделения по задачам у них нет.
Задача принимается, только если проходят все проверки ниже. Каждая - одна команда. Запускайте их не в последний день: чем раньше, тем дешевле правки.
cmake --preset dev
cmake --build --preset dev
ctest --preset devКомпилятор настроен строго: любое предупреждение останавливает сборку. Это не придирка - включённые проверки ловят настоящие ошибки:
| Проверка | Что находит |
|---|---|
-Wconversion |
Неявная потеря данных: double в int, int в int16_t. Число тихо меняется |
-Wsign-conversion |
Отрицательный int превращается в огромный size_t, и проверка i < v.size() перестаёт работать |
-Wshadow |
Локальная переменная закрывает поле класса, присваивание уходит не туда |
-Wnon-virtual-dtor |
Удаление наследника через указатель на базу без виртуального деструктора |
-Woverloaded-virtual |
Метод наследника прячет виртуальный метод базы вместо переопределения |
На Windows тот же смысл несут /W4 /WX /permissive-.
Пока пишете код, остановку на предупреждениях можно временно снять пресетом relaxed.
Сдавать нужно с dev.
cmake --preset relaxed && cmake --build --preset relaxedcmake --preset asan
cmake --build --preset asan
ctest --preset asanСанитайзер - это дополнительные проверки, которые компилятор встраивает прямо в программу. При ошибке программа падает с подробным отчётом (файл, строка, стек вызовов) в тот момент, когда ошибка произошла, вместо того чтобы "иногда работать, а иногда нет". Находит:
- выход за границы массива, вектора или строки;
- обращение к освобождённой памяти и повторное освобождение;
- утечки памяти (только Linux);
- переполнение знакового целого, сдвиг на число разрядов больше размера типа, разыменование нулевого указателя. Всё это - неопределённое поведение, то есть по стандарту программа имеет право сделать что угодно.
| Система | Ошибки памяти | Неопределённое поведение | Утечки |
|---|---|---|---|
| Linux | да | да | да |
| macOS | да | да | нет |
| Windows (MSVC) | да | нет | нет |
Из-за этой таблицы полный набор проверок вы получаете в GitHub Actions, где всё гоняется на Linux.
cmake --preset tidy
cmake --build --preset tidyclang-tidy читает код, не запуская его, и находит подозрительные места по набору правил
из файла .clang-tidy.
Установка: Ubuntu sudo apt install clang-tidy, macOS brew install llvm
(и добавить в PATH путь из brew --prefix llvm), Windows - компонент
"C++ Clang tools for Windows" в установщике Visual Studio.
cmake --preset coverage
cmake --build --preset coverage
ctest --preset coverageПоказывает, какие строки вашего кода ни разу не выполнились за время тестов.
Для HTML-отчёта нужен lcov (sudo apt install lcov / brew install lcov):
lcov --capture --directory build/coverage --output-file coverage.info --ignore-errors mismatch,gcov
lcov --remove coverage.info '/usr/*' '*/_deps/*' '*_test.cpp' '*/main.cpp' --output-file coverage.info --ignore-errors unused
lcov --list coverage.info
genhtml coverage.info --output-directory coverage-htmlОтчёт откроется в coverage-html/index.html. На Windows покрытие не собирается -
берите отчёт из GitHub Actions.
cmake --build --preset dev --target format-check # проверить форматирование
cmake --build --preset dev --target format # исправить форматированиеФорматирование делает clang-format по файлу .clang-format. Руками ничего выравнивать
не нужно, спорить о пробелах тоже. Ключевое: открывающая скобка на той же строке,
отступ 2 пробела, тело if/for/while всегда в фигурных скобках.
Имена проверяет clang-tidy (пресет tidy). Ориентир - стандартная библиотека:
push_back, to_string, size. Официального стандарта оформления в C++ нет,
поэтому курс следует стилю того, что вы расширяете.
| Что | Правило | Пример |
|---|---|---|
| Классы, структуры, перечисления | CamelCase |
BitArray, WordCount |
| Функции и методы | lower_case |
push_back, split_words |
| Переменные и параметры | lower_case |
num_bits |
| Поля класса | lower_case_ |
size_, data_ |
Константы constexpr |
kCamelCase |
kWordBits |
| Пространства имён | lower_case |
word_count |
| Макросы | UPPER_CASE |
С большой буквы пишутся только типы. Функций с большой буквы в курсе нет.
Установка clang-format: Ubuntu sudo apt install clang-format,
macOS brew install llvm, Windows - вместе с компонентом
"C++ Clang tools for Windows" в установщике Visual Studio.
В .github/workflows/ci.yml уже настроен запуск всех проверок при каждом push
и Pull Request: сборка и тесты на трёх платформах, санитайзеры, clang-tidy,
покрытие с публикацией HTML-отчёта. Настраивать ничего не нужно - запушьте ветку
и смотрите вкладку Actions.
Отдельный пресет на Google Benchmark. Библиотека тяжёлая, поэтому качается только когда нужна - обычные сборки от неё не страдают.
cmake --preset bench
cmake --build --preset bench
./build/bench/tasks/example/example_benchПресет bench собирает в Release. В отладочной сборке измерять нечего: без оптимизаций
числа не имеют отношения к реальности.
Подключается к задаче одним параметром:
oop_task(NAME task2 SOURCES bit_array.cpp TESTS bit_array_test.cpp BENCH bit_array_bench.cpp)Компилятор выбрасывает вычисления, результат которых не используется. Замер вида
for (auto _ : state) {
a.method(); // результат никуда не идёт
}измеряет пустой цикл и покажет 0 наносекунд. Это не скорость, это отсутствие кода. Правильно - показать результат компилятору:
for (auto _ : state) {
benchmark::DoNotOptimize(a.method());
}Вторая ловушка того же рода: если компилятор видит точный тип объекта, он заменяет виртуальный вызов обычным (девиртуализация), и сравнение теряет смысл. Вызывать нужно через указатель или ссылку на базовый класс.
Смотрите tasks/example/example_bench.cpp: там измерены стоимость виртуального вызова,
создания объекта в куче против стека, броска исключения и обращения к часам.
| Файл | Назначение |
|---|---|
CMakeLists.txt |
Корневые правила сборки. Папки задач подключаются автоматически |
CMakePresets.json |
Готовые наборы флагов: dev, relaxed, asan, coverage, tidy, bench, release |
cmake/OopFlags.cmake |
Флаги предупреждений, санитайзеров и покрытия по платформам |
cmake/OopTask.cmake |
Функция oop_task() - описание одной задачи |
.clang-tidy |
Правила статического анализа и именования |
.clang-format |
Правила форматирования |
cmake/OopFormat.cmake |
Цели format и format-check |
.github/workflows/ci.yml |
Автоматический запуск всех проверок на GitHub |
tasks/example/ |
Задача-образец: копируйте её под свои задачи |
tasks/example/example_bench.cpp |
Образец замеров производительности |
| Симптом | Причина и решение |
|---|---|
CMake 3.21 or higher is required |
Старый CMake, обновите: https://cmake.org/download/ |
| Сборка падает на предупреждении в вашем коде | Это и есть проверка. Исправьте причину, а не ставьте приведение типа "чтобы замолчало" |
Новая папка в tasks/ не собирается |
Выполните cmake --preset dev заново: список папок читается при настройке |
clang-tidy не найден |
Установите (см. пункт 3) или пользуйтесь остальными пресетами |
На Windows не находится .exe |
Ищите в build\dev\tasks\<задача>\Debug\: Visual Studio раскладывает по подпапкам конфигураций |
detect_leaks is not supported |
Поиск утечек есть только на Linux, на macOS и Windows это нормально |
| Долгая первая сборка | GoogleTest скачивается и собирается один раз на каждый пресет |