Включишь в проекте C++20 и строгие предупреждения; на примере std::format увидишь, что бывает, когда версия языка слишком старая.
С этой главы проект открываем и собираем в Visual Studio. Если Visual Studio закрыта, запусти её и открой папку проекта: FileФайл → OpenОткрыть → FolderПапка → D:\dev\MyEngine. В списке конфигураций в верхней полосе окна, слева от кнопки запуска без отладки
Контурный зелёный треугольник в верхней полосе окна Visual Studio, правее списка app.exe. Кнопка запускает программу без отладчика, как и Ctrl+F5:
, должно стоять Debug: эту конфигурацию Visual Studio взяла из пресета сборки debug.
Зачем тогда CMake, пресеты и консоль, если можно было создать проект в Visual Studio
Проект, который создаёт мастер Visual Studio, описан файлами .slnx и .vcxproj, и такие файлы понимает в основном Visual Studio. Наш проект описан в CMakeLists.txt и CMakePresets.json, а Visual Studio просто открывает эти файлы. Что это даёт:
Проще подключать библиотеки. Почти все библиотеки C++ собираются через CMake, и такая библиотека подключается к проекту парой строк в CMakeLists.txt. В проекте Visual Studio пути к заголовкам и файлам .lib пришлось бы вписывать вручную в свойства проекта, отдельно для Debug и Release.
Проект не привязан к Visual Studio. Тот же CMakeLists.txt откроют другие редакторы, например CLion и VS Code
CLion — среда разработки для C++ от компании JetBrains, VS Code — бесплатный редактор кода от Microsoft. Обе программы умеют открывать проекты CMake так же, как Visual Studio.
, и соберут другие компиляторы
GCC и Clang — компиляторы C++, такие же, как MSVC в Visual Studio. На Linux обычно собирают через GCC, на macOS — через Clang.
на Linux и macOS.
Проект собирается без человека. Можно настроить сервер
Например, GitHub Actions: сервис GitHub, который после каждого изменения, выложенного на GitHub, сам скачивает проект, собирает его и сообщает, если сборка сломалась.
, который сам собирает проект после каждого изменения. Запустить программу сервер не может, зато выполнит те же команды, что ты набирал в консоли, и возьмёт настройки из того же CMakePresets.json.
А консоль в первых главах показала, что Visual Studio делает за тебя: настраивает проект, запускает компилятор и линкер. Когда сборка в Visual Studio сломается, ты будешь знать, на каком шаге искать причину.
std::format
В этой главе проект получает две настройки: версию языка C++20 и строгие предупреждения. Чтобы увидеть, зачем нужна версия языка, возьмём функцию std::format: она есть только в C++20, и сборка по умолчанию на ней ломается. Движку функция тоже пригодится: через неё программа будет сообщать значения, например размер окна или время кадра.
Обычно текст со значениями печатают так: std::cout << "Window " << width << "x" << height << "\n";. Строка дробится на куски. В std::format строку пишут целиком, а на местах значений ставят {}, значения идут следом:
std::format(“Window {}x{}\n”, width, height)
Первая пара скобок получает значение width, вторая пара — значение height. При ширине 1280 и высоте 720 программа напечатает Window 1280x720.
После двоеточия в скобках задают вид значения. Запись {:.2f} оставляет две цифры после запятой: 3.14159 превращается в 3.14. Так удобно печатать время кадра.
Сборка не пройдёт. На вкладке Error ListСписок ошибок будет предупреждениеSTL4038: The contents of <format> are available only with C++20 or later., и ошибки C2039, C3861 и C2593в строке 6 файла main.cpp. Ошибки C2039 и C3861 говорят, что в std нет имени format, а C2593 появляется следом: без std::format компилятор не может разобрать строку печати. Предупреждение называет причину: std::format есть только в C++20 и новее.
У каждой версии C++ свой стандарт языка, и в CMakeLists.txt версия пока не указана. Тогда компилятор MSVC берёт свою версию по умолчанию — C++14.
Предупреждение указывает не на main.cpp, а на строку 42 файла format. Это строка в заголовочном файле, который #include <format> вставил в программу: она проверяет версию языка и сообщает, что для std::format версия слишком старая.
Читай сообщения сборки в порядке появления. В окне OutputВывод сообщения идут в том порядке, в каком компилятор их написал, и первым стоит предупреждение STL4038: оно называет причину. Ошибки C2039, C3861 и C2593 в main.cpp, которые идут ниже, — следствие.
C++20 в CMakeLists.txt
Сборка сломалась не из-за кода в main.cpp: код написан верно, а компилятор собирает по старому стандарту C++14. Версию языка записывают не в коде, а в настройках проекта, то есть в CMakeLists.txt. Сейчас мы напишем там, что программе app нужен C++20, и CMake сам передаст компилятору нужный флаг.
Если не получилось3
Cannot specify compile features for target "ap" which is not built by this project. Вместо ap в кавычках будет слово, которое ты написал в скобках строки target_compile_features первым. Сообщение видно в окне Output.
Почему
CMake не нашёл цель с таким именем. Имя в target_compile_features должно совпадать с первым словом в add_executable буква в букву. Та же ошибка будет, если строка стоит выше add_executable: CMake читает файл сверху вниз, и цель там ещё не создана.
Что сделать
Исправь имя на app, поставь строку ниже add_executable(app main.cpp) и сохрани файл.
target_compile_features specified unknown feature "cxx_std20" for target "app".
Почему
CMake не знает такой возможности компилятора: в слове cxx_std20 пропущен знак подчёркивания.
Что сделать
Напиши cxx_std_20 с двумя знаками подчёркивания и сохрани файл.
C++20 включён, а ошибки C2039 и C3861 про format остались.
Почему
Если на вкладке Error ListСписок ошибок нет предупреждения STL4038, то версия языка уже верная, и причина другая: в main.cpp нет строки #include <format>. Без этой строки компилятор не знает std::format при любой версии языка. Сообщения те же, что и при C++14, поэтому причину отличает только предупреждение STL4038.
Что сделать
Добавь первой строкой main.cpp#include <format>, сохрани файл и собери проект
Нажми Ctrl+Shift+B или выбери в меню BuildСборка → Build AllСобрать все. Visual Studio только соберёт проект и ничего не запустит. Результат сборки смотри в окне OutputВывод и на вкладке Error ListСписок ошибок.
.
Строгие предупреждения
Предупреждение сообщает о месте в коде, которое компилятор переводит, но считает подозрительным. По умолчанию MSVC собирает с уровнем предупреждений /W1: это второй снизу из пяти уровней, от /W0 до /W4, и на нём компилятор о многом молчит. Для движка включим уровень /W4: чем раньше компилятор заметит подозрительное место, тем меньше времени уйдёт на поиск ошибки.
Здесь нужна другая команда CMake, не та, что включала C++20. Команда target_compile_features, которой ты включил C++20, говорила, что нужно программе: «C++20». Какой флаг для этого передать, CMake решал сам для каждого компилятора. Для уровня предупреждений в CMake нет слова вроде cxx_std_20, поэтому команда target_compile_options передаёт компилятору сам флаг, который ты пишешь своими руками.
Две подробности про предупреждения:
Предупреждение показывается только после сборки, в которой файл компилировался. Если бы ты собрал проект ещё раз, не удаляя переменную, main.cpp не перекомпилировался бы, и предупреждение пропало бы с вкладки Error ListСписок ошибок, хотя переменная осталась в коде.
Если переменная нужна, но может остаться неиспользованной, например когда значение переменной смотрят только в отладчике, поставь перед строкой с переменной атрибут
Пометка в двойных квадратных скобках, которая что-то сообщает компилятору о следующем объявлении. [[maybe_unused]] появился в C++17 и значит «это может не использоваться, так задумано».
[[maybe_unused]]: строка [[maybe_unused]] int unused = 42; собирается без предупреждения.
Итог
Программа печатает Hello, Engine! через std::format, проект собирается по стандарту C++20, а компилятор сообщает о подозрительных местах на уровне /W4. Так выглядят оба файла после главы: