cxxmodules: use synthetic targets to provide modules within a project as well
When using C++20 modules in shared libraries on MSVC (using the Ninja generator), __declspec(dllexport)
declarations
end up getting exported from module interfaces. These declarations then get imported by consumers of the module.
This works, but is not ideal, as it incurs an extra indirection for calls to functions from the library.
(See https://stackoverflow.com/a/74444920 for a more detailed look at this).
Ideally we would have:
- the code compiled into the library sees
__declspec(dllexport)
declarations - the code using the library sees
__declspec(dllimport)
declarations
With headers, we have generate_export_header
to achieve this. To get the same approach working with modules,
the module interfaces of the library would need to be compiled a second time for its consumers.
After reading about CXX_MODULES
file-sets on IMPORTED
targets, it sounds like the synthesized target might do just that:
add_library(mod SHARED src/mod.cpp)
target_sources(mod PRIVATE FILE_SET CXX_MODULES FILES src/mod.ixx)
target_compile_features(mod PUBLIC cxx_std_20)
target_compile_definitions(mod PUBLIC MOD_SHARED PRIVATE MOD_EXPORTS)
add_library(mod_import INTERFACE IMPORTED)
target_sources(mod_import INTERFACE FILE_SET CXX_MODULES FILES src/mod.ixx)
target_link_libraries(mod_import INTERFACE mod)
set_target_properties(mod_import PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON)
But for some reason, the synthesized target only contains the module map & dyndep steps,
no .ifc file is actually generated (and .bmi
files are referenced in the modmap file instead of .ifc
).
The consuming target's modmap then ends up as:
-reference mod=
Which doesn't work.
The workaround I found was adding the module interface to the consuming target manually:
add_executable(main src/main.cpp)
target_sources(main PRIVATE FILE_SET CXX_MODULES FILES src/mod.ixx)
target_link_libraries(main PRIVATE mod)
which succeeds and uses the "dllimport" version of the interface (despite some errors
CMake Error: Disagreement of the location of the 'mod' module. Location A: 'CMakeFiles\mod.dir\mod.ifc' via by-name; Location B: 'CMakeFiles\main.dir\mod.ifc' via by-name.
CMake Error: Disagreement of the location of the 'mod' module. Location A: 'CMakeFiles\mod.dir\mod.ifc' via by-name; Location B: 'CMakeFiles\main.dir\mod.ifc' via by-name.
Neither of these two approaches really seems like the "intended" way to go, though.
Versions used: CMake 3.28.1, ninja 1.11.1, msvc 19.38