-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add RFC about C++20 modules support #2043
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,199 @@ | ||||||
| # C++20 Modules Support for oneTBB | ||||||
|
|
||||||
| ## Introduction | ||||||
|
|
||||||
| C++20 introduced modules as a modern alternative to the traditional header-based inclusion | ||||||
| model. Modules offer several advantages over headers: | ||||||
|
|
||||||
| - Modules are compiled once and their binary interface is reused, avoiding repeated parsing | ||||||
| and preprocessing of header files. | ||||||
| - Modules provide better encapsulation — macros, internal declarations, and implementation | ||||||
| details do not leak into the consumer's translation unit. | ||||||
|
|
||||||
| As the C++ ecosystem gradually adopts modules, oneTBB should provide a module interface to | ||||||
| allow users to use the library using `import tbb;` instead of `#include <oneapi/tbb.h>`. | ||||||
|
|
||||||
| ## Proposal | ||||||
|
|
||||||
| ### ABI non-breaking style with using-declaration | ||||||
|
|
||||||
| The proposed approach is to provide a wrapper module that includes the existing headers in | ||||||
| the global module fragment and re-exports public symbols. | ||||||
|
|
||||||
| The module interface unit (e.g., `tbb.cppm`) would follow this structure: | ||||||
|
|
||||||
| ```cpp | ||||||
| module; | ||||||
|
|
||||||
| // Global module fragment — include all public headers | ||||||
| #include <oneapi/tbb.h> | ||||||
|
|
||||||
| export module tbb; | ||||||
|
|
||||||
| // Re-export public API via using-declarations | ||||||
| export namespace tbb { | ||||||
| // Parallel algorithms | ||||||
| using tbb::parallel_for; | ||||||
| using tbb::parallel_reduce; | ||||||
| using tbb::parallel_scan; | ||||||
| using tbb::parallel_sort; | ||||||
| using tbb::parallel_invoke; | ||||||
| using tbb::parallel_pipeline; | ||||||
| using tbb::parallel_for_each; | ||||||
|
|
||||||
| // other TBB API | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| The proposed approach has several advantages over other alternatives: | ||||||
|
|
||||||
| - It does not require the modification of existing headers, as the module includes them as | ||||||
| part of global module fragment and then selectively export the necessary `using` | ||||||
| declarations. Thus, the development can continue in headers. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| - Since the API is declared as part of global module fragment, the name of the module will | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| not participate in the name mangling, making translation units compiled with modules, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| compatible with the units compiled with regular headers. | ||||||
|
|
||||||
| The disadvantage of this approach is an obligation to update the module interface unit each time new API is added. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| ### CMake integration | ||||||
|
|
||||||
| The library must remain at C++11 as the minimum standard, hence the module source cannot be | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "must remain at C++11" is because of a project policy, not a technical reason. We may for example later, change to C++17 as the minimum standard as a policy change. As written, its unclear if you are stating a design constraint or a technical constraint. |
||||||
| compiled as part of the oneTBB build. Additionally, currently the built module constrains | ||||||
| the consumer to use the specific version of the C++ standard: the one that was used during | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| the module build. Thus, the `.cppm` file is installed as a source file and compiled on the | ||||||
| consumer side. | ||||||
|
|
||||||
| ```cmake | ||||||
| find_package(TBB REQUIRED) | ||||||
|
|
||||||
| add_executable(myapp main.cpp) | ||||||
| target_link_libraries(myapp PRIVATE TBB::tbb) | ||||||
| # For example, tbb.cppm is located under include/modules | ||||||
| get_target_property(_tbb_include_dir TBB::tbb INTERFACE_INCLUDE_DIRECTORIES) | ||||||
| target_sources(myapp PRIVATE | ||||||
| FILE_SET cxx_modules TYPE CXX_MODULES | ||||||
| BASE_DIRS ${_tbb_include_dir} | ||||||
| FILES ${_tbb_include_dir}/modules/tbb.cppm | ||||||
| ) | ||||||
| ``` | ||||||
|
|
||||||
| In the future, `TBBConfig.cmake` could provide a helper function (e.g., | ||||||
| `tbb_enable_cxx20_modules(myapp)`) to reduce this boilerplate. | ||||||
|
|
||||||
| The consumer compiles the `.cppm` as part of their own target with their own flags. Name | ||||||
| mangling is unchanged, so the consumer links against the same `libtbb.so` / `tbb.lib` | ||||||
| regardless of whether they use modules or headers. | ||||||
|
|
||||||
| ## Alternatives Considered | ||||||
|
|
||||||
| ### 1. ABI non-breaking style with language linkage block | ||||||
|
|
||||||
| Instead of re-exporting symbols via `using`-declarations, library headers are included | ||||||
| inside an `extern "C++"` language linkage block within the module purview, and all | ||||||
| third-party/system headers are included in the global module fragment. | ||||||
|
|
||||||
| ```cpp | ||||||
| module; | ||||||
|
|
||||||
| // Global module fragment — system and third-party headers only | ||||||
| #include <cstddef> | ||||||
| #include <atomic> | ||||||
| #include <functional> | ||||||
| // ... all standard/third-party headers used by TBB | ||||||
|
|
||||||
| export module tbb; | ||||||
|
|
||||||
| #define __TBB_IN_MODULE_USE | ||||||
| extern "C++" { | ||||||
| #include "oneapi/tbb.h" | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Declarations within an `extern "C++"` block inside the module purview are actually attached to | ||||||
| the global module fragment by the standard. An important detail here is that TBB headers will need | ||||||
| to define some macro (e.g. `__TBB_CXX20_EXPORT`) if `__TBB_IN_MODULE_USE` is defined. The definition | ||||||
| of `__TBB_IN_MODULE_USE` would also mean that all inclusions of third-party headers should be | ||||||
| excluded from TBB headers. | ||||||
|
|
||||||
| **Pros:** | ||||||
| - No need to maintain explicit `using`-declarations for every public symbol. | ||||||
| - To export an API you simply add `__TBB_CXX20_EXPORT` to the declaration. | ||||||
| - ABI-compatible with header-based consumers. | ||||||
|
|
||||||
| **Cons:** | ||||||
| - Requires careful separation of system/third-party headers from TBB headers. If TBB header | ||||||
| includes a system header that was not already in the GMF, it can cause compilation errors. | ||||||
| - Requires the modification of all TBB headers to add conditional `export` keyword to public | ||||||
| declarations and stripping the inclusion of third-party headers from them. | ||||||
|
|
||||||
| ### 2. ABI breaking style | ||||||
|
|
||||||
| Same header-inclusion approach, but library headers are included directly in the module | ||||||
| purview without an `extern "C++"` block. This causes the module name to participate in | ||||||
| symbol mangling. | ||||||
|
|
||||||
| ```cpp | ||||||
| module; | ||||||
|
|
||||||
| // Global module fragment — system and third-party headers only | ||||||
| #include <cstddef> | ||||||
| #include <atomic> | ||||||
| #include <functional> | ||||||
| // ... all standard/third-party headers used by TBB | ||||||
|
|
||||||
| export module tbb; | ||||||
|
|
||||||
| #define __TBB_IN_MODULE_USE | ||||||
| #include <oneapi/tbb.h> | ||||||
| ``` | ||||||
|
|
||||||
| **Pros:** | ||||||
| - No need to maintain explicit `using`-declarations for every public symbol. | ||||||
| - To export an API you simply add `__TBB_CXX20_EXPORT` to the declaration. | ||||||
| - The library could be compiled to include symbols with and without module name mangled. That would | ||||||
| require the consumer of the library to consistently use either TBB headers, or TBB module. | ||||||
|
|
||||||
| **Cons:** | ||||||
| - The previously mentioned advantage can be seen as disadvantage. The user must choose either modules | ||||||
| or headers because they cannot coexist in the same program. It also might cause problems when the | ||||||
| consumer's application uses TBB as module and other third-party that uses TBB, but as headers, or | ||||||
| vice versa. | ||||||
| - Same cons as for ABI non-breaking style described above. | ||||||
|
|
||||||
| ## Open Questions | ||||||
|
|
||||||
| 1. Should the module be named `tbb`, `oneapi.tbb`, or `onetbb`? | ||||||
|
|
||||||
| 2. Where should the module interface unit live? Options include `include/modules/tbb.cppm`, | ||||||
| `src/tbb/tbb.cppm`. | ||||||
|
|
||||||
| 3. What is the install destination for the module source file? Should it live under | ||||||
| `share/tbb/modules/`, `include/modules/`, or next to `TBBConfig.cmake`? | ||||||
|
|
||||||
| 4. Should the module be split into partitions (e.g., `tbb:algorithms`, `tbb:containers`, | ||||||
| `tbb:flow_graph`) to organize the `using`-declarations? | ||||||
| Partitions are internal to the module and not importable by consumers, but could | ||||||
| improve maintainability of the module interface. Alternatively, should multiple | ||||||
| fine-grained submodules (e.g., `tbb.flow_graph`, `tbb.containers`) be provided so | ||||||
| that consumers can import only what they need? | ||||||
|
|
||||||
| 5. How should module-based consumption be tested? Options include a dedicated test | ||||||
| that uses `import tbb;` instead of `#include`, or running the existing test suite | ||||||
| with module imports. Should whitebox tests be covered in the latter scenario? | ||||||
|
|
||||||
| 6. How to provide preview functionality with modules? Should it be a separate `tbb.preview` module | ||||||
| or should the `tbb` module be compiled with defined `TBB_PREVIEW_*` macros as needed? | ||||||
|
|
||||||
| 7. How to support public macros such as `TBB_VERSION` or feature-test macros? Some of the options: | ||||||
| - Replace them by inline variables where possible. | ||||||
| - Resort to inclusion of `tbb/version.h` header. | ||||||
|
|
||||||
| ## Exit Criteria | ||||||
|
|
||||||
| The following conditions should be met before this feature graduates from | ||||||
| experimental to fully supported: | ||||||
|
|
||||||
| 1. The open questions above are resolved. | ||||||
| 2. CMake and compiler support should improve to the point where modules support is not at | ||||||
| experimental stage. | ||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.