Я пытаюсь использовать модули, чтобы немного ускорить сборку наших окон. По моим первым впечатлениям, модули C++20 определенно быстрее, чем PCH (по крайней мере, в Windows).
В настоящее время я сосредоточен только на Windows с последней версией MSVC 17 и последней версией CMake. Я также использую import std
, который доступен только на C++23.
Я знаю, что эти функции являются передовыми, но это первая попытка, которая не использовалась ни в одной продуктивной системе. (Мне нужно сохранить обратную совместимость, хотя и с «классическими» включениями)
Поскольку MSVC17 имеет хорошую поддержку рабочих модулей, а CMake добавил стандарт импорта в свою последнюю версию, я бы хотел попробовать.
Теперь проблемы, с которыми я столкнулся, - это конфликты имен.
Итак, поскольку я использую import std;
, я бы хотел избежать всех включений stl.
Модифицировать наш код не составляет большого труда, настоящая задача — упаковать существующие сторонние модули (например, fmt и nlohmann_json) в модули, поскольку их заголовки включают несколько заголовков std (которые вызывают конфликты имен с import std;
).
Я смотрел рассказы Даниэлы Энгерт о модулях C++20 на некоторых конференциях CppCon и ознакомился с ее примерами. https://github.com/DanielaE/asio/tree/d212efd69aedf221475dc2434c45a6baeee10d16/asio/module По сути, она включила в экспорт сторонний заголовок. Однако это не сработало для меня.
// Global module fragment where #includes can happen
module;
//inlcuded headers will not be exported to the client
// first thing after the Global module fragment must be a module command
export module nlohmann_json;
extern "C++"
{
export
{
#include <nlohmann/json.hpp>
}
}
module: private;
//possbile to include headers here, but they will not be exported to the client
Это моя тестовая программа
#include <gtest/gtest.h>
import nlohmann_json;
TEST(CxxModuleTest, nlohman_json)
{
nlohmann::json j = {
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{"answer", {
{"everything", 42}
}},
{"list", {1, 0, 2}},
{"object", {
{"currency", "USD"},
{"value", 42.99}
}}
};
EXPECT_EQ(j["pi"], 3.141);
EXPECT_EQ(j["happy"], true);
EXPECT_EQ(j["name"], "Niels");
EXPECT_EQ(j["nothing"], nullptr);
EXPECT_EQ(j["answer"]["everything"], 42);
EXPECT_EQ(j["list"][0], 1);
EXPECT_EQ(j["list"][1], 0);
EXPECT_EQ(j["list"][2], 2);
EXPECT_EQ(j["object"]["currency"], "USD");
EXPECT_EQ(j["object"]["value"], 42.99);
}
Однако это не компилируется. Я получаю
'#include <filename>' in the purview of module 'module-name-1' appears erroneous. Consider moving that directive before the module declaration, or replace the textual inclusion with 'import <filename>;'.
Я закончил с этим
// Global module fragment where #includes can happen
module;
//inlcuded headers will not be exported to the client
#include <nlohmann/json.hpp>
// first thing after the Global module fragment must be a module command
export module nlohmann_json;
export
{
namespace nlohmann::json_abi_v3_11_2::detail
{
using ::nlohmann::json_abi_v3_11_2::detail::json_sax_dom_callback_parser;
using ::nlohmann::json_abi_v3_11_2::detail::json_sax_dom_parser;
}
namespace nlohmann
{
using ::nlohmann::basic_json;
using ::nlohmann::json;
using ::nlohmann::json_sax;
using ::nlohmann::detail::json_sax_dom_parser;
using ::nlohmann::detail::json_sax_dom_callback_parser;
}
}
module: private;
//possbile to include headers here, but they will not be exported to the client
Однако это попытка и ошибка. Мне нужно добавить все типы, пока программа не скомпилируется.
=>Мой вопрос: Есть ли лучший (более общий) способ?
спасибо, ребята, за вашу помощь :)
Вы можете export import
заголовочный файл в интерфейсном блоке модуля, чтобы экспортировать в него все символы.
// in .ixx file, module interface unit
export module nlohmann_json;
export import <nlohmann/json.hpp>;
// in .cpp file
import nlohmann_json;
// use nlohmann/json here
Вероятно, это самый простой способ портировать устаревшие заголовки в модули до тех пор, пока вы не сможете написать для них подходящий модуль интерфейса модуля, в котором вы экспортируете только те компоненты, которые вам действительно нужны.
Обратите внимание, что ограничение экспортируемых символов в будущем станет критическим изменением API, поэтому авторам библиотек не следует делать это слишком часто.
Пример CMakeLists.txt:
cmake_minimum_required(VERSION 3.28)
set(CMAKE_CXX_STANDARD 20)
set(JSON_BuildTests OFF CACHE INTERNAL "")
project(cpp_json)
add_subdirectory(external/json) # nlohmann_json directory
add_executable(cpp_json main.cpp)
target_sources(cpp_json
PUBLIC
FILE_SET modules TYPE CXX_MODULES
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/module
FILES module/nlohmann_json.ixx
)
target_link_libraries(cpp_json PUBLIC nlohmann_json::nlohmann_json)
поскольку файлу .ixx
не нужны файлы .cpp
, вы можете создать для него библиотеку интерфейса.
cmake_minimum_required(VERSION 3.28)
set(CMAKE_CXX_STANDARD 20)
set(JSON_BuildTests OFF CACHE INTERNAL "")
project(cpp_json)
add_subdirectory(external/json) # nlohmann_json directory
add_library(nlohmann_json_interface) # linking target for import
target_sources(nlohmann_json_interface
PUBLIC
FILE_SET modules TYPE CXX_MODULES
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/module
FILES module/nlohmann_json.ixx
)
target_link_libraries(nlohmann_json_interface PUBLIC nlohmann_json::nlohmann_json)
add_executable(cpp_json main.cpp)
# only need my interface target
target_link_libraries(cpp_json nlohmann_json_interface)
Конечно, вы правы, мне нужно экспортировать все символы, иначе я сломаю свой API. Вот почему мне не нравится мой подход. Однако другого рабочего способа я не нашел :(
@JHeni, он отлично работает на моей машине с использованием Visual Studio 17.11.0 Preview 2. Вы уверены, что какая бы цель ни имела этот .ixx
файл, она также использует target_link_libraries(something PUBLIC nlohmann_json::nlohmann_json)
, чтобы каталоги включения были установлены правильно?
@JHeni также работает в Visual Studio 17.9.6, я добавил CMakeLists.txt
извини. Похоже, я делаю что-то не так. Я создал новый пустой проект cmake и скопировал первый фрагмент cmake (модуль и основной в пределах одной цели), и я все еще получаю hederunit for nlohmann/json.hpp
не найден, мне пришлось удалить add_subdirectory(external/json) # nlohmann_json directory
В моем фиктивном проекте external/json - это папка только с заголовком, а не отдельный модуль cmake
@JHeni, если вы используете библиотеку только для заголовков, у вас должна быть target_include_directories(cpp_json PUBLIC external/json)
или что-то в этом роде, чтобы добавить каталог включения заголовка, который будет общедоступен для cmake.
да, я сделал это. (Сначала я пропустил это и получил ошибку «файл не найден»;)) Ребята, вы действительно используете проект cmake или решение VS? export import <nlohmann/json.hpp>;
это не шапка? Блоки заголовков пока не поддерживаются Cmake (с другой стороны, VS имеет поддержку блоков заголовков)
@JHeni, я работаю с cd build
, затем cmake ..
, затем cmake --build .
, не могли бы вы обновить свою версию Visual Studio? На панели инструментов используйте Help -> check for updates
, вы можете найти весь проект в этом zip-файле file.io/6Zgfnh3L85YU. Если на вашем устройстве произойдет сбой, значит, у вас проблема с установкой Visual Studio.
ах, спасибо за разъяснения. Когда я строю из CLI, это тоже работает. Когда я импортирую этот проект как проект cmake в VS, он не работает: O
эй, спасибо за твой ответ. К сожалению, это дает мне заголовок ошибки компилятора
nlohmann/json.hpp>;
, который не найден. Я что-то пропустил?