Created
September 1, 2025 14:47
-
-
Save kylehaokunwu/431efd647efb9393494b432299b1dee7 to your computer and use it in GitHub Desktop.
GSoC 2025 Final Report – Exposing HPX with C++20 Modules
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| gsoc-final-report.md | |
| --- | |
| # GSoC 2025 Final Report | |
| **Project**: Exposing HPX using C++20 Modules | |
| **Contributor**: Haokun Wu | |
| **Org**: STE||AR Group | |
| **Primary PR**: [https://github.com/STEllAR-GROUP/hpx/pull/6761](https://github.com/STEllAR-GROUP/hpx/pull/6761) | |
| ## Project goals | |
| Move HPX toward C++20 modules while keeping backward compatibility: | |
| * Existing users can keep `#include <hpx/...>` with no changes. | |
| * Users can alternatively write `import HPX.Core;` and use the same APIs. | |
| Secondary goals: | |
| 1. export both template and non-template symbols correctly; | |
| 2. establish minimal, maintainable build/macro mechanics to expose more libraries over time. | |
| --- | |
| ## What I did | |
| ### 1) Design choice: umbrella module, not 1:1 copies | |
| A common approach is duplicating headers into `.ixx` files. That adds maintenance overhead. Instead, each HPX library gets a single umbrella `.ixx` that re-exports its headers. For core: | |
| ```cpp | |
| // libs/core/hpx_core.ixx | |
| module; // global fragment | |
| #include <algorithm> | |
| #include <string> | |
| #include <cstdint> | |
| #define HPX_BUILD_MODULE | |
| #include <hpx/config.hpp> | |
| export module HPX.Core; | |
| // headers that define the public surface | |
| #include <hpx/modules/version.hpp> | |
| ``` | |
| This keeps the tree lean, preserves header inclusion, and allows `import HPX.Core;`. | |
| ### 2) Symbol export mechanics (templates vs non-templates) | |
| Header builds rely on `HPX_CORE_EXPORT` (DLL/export visibility). For modules: | |
| * Templates can be exported with `export`. | |
| * Non-templates must use `export extern "C++"` so the linker finds the compiled symbol. | |
| * To avoid double attributes, when building a module we undefine and empty out `HPX_CORE_EXPORT`. | |
| Minimal macro surface (from `export_definitions.hpp`): | |
| ```cpp | |
| #if defined(HPX_BUILD_MODULE) | |
| # if defined(HPX_HAVE_ELF_HIDDEN_VISIBILITY) | |
| # undef HPX_CORE_EXPORT | |
| # define HPX_CORE_EXPORT /* empty */ | |
| # endif | |
| # define HPX_CORE_MODULE_EXPORT_EXTERN export extern "C++" | |
| #else | |
| # define HPX_CORE_MODULE_EXPORT_EXTERN extern "C++" | |
| #endif | |
| #define HPX_CORE_MODULE_EXPORT \ | |
| HPX_CORE_MODULE_EXPORT_EXTERN HPX_CORE_EXPORT | |
| ``` | |
| Use `HPX_CORE_MODULE_EXPORT` in place of `HPX_CORE_EXPORT` when a declaration is exposed through a module. | |
| **Before (headers):** | |
| ```cpp | |
| HPX_CORE_EXPORT std::uint8_t agas_version(); | |
| ``` | |
| **After (module):** | |
| ```cpp | |
| HPX_CORE_MODULE_EXPORT std::string build_date_time(); | |
| ``` | |
| ### 3) CMake integration (two key pieces) | |
| **a) Register the `.ixx` as a C++ module interface and install BMIs** | |
| Essentials only (from `libs/CMakeLists.txt`): | |
| ```cmake | |
| if(HPX_WITH_CXX_MODULES) | |
| target_sources(hpx_${lib} | |
| PUBLIC FILE_SET hpx_${lib}_public_sources TYPE CXX_MODULES FILES | |
| ${CMAKE_CURRENT_SOURCE_DIR}/${lib}/hpx_${lib}.ixx | |
| ) | |
| set(module_installation | |
| FILE_SET hpx_${lib}_public_sources DESTINATION ${CMAKE_INSTALL_LIBDIR}/cxx/miu | |
| CXX_MODULES_BMI DESTINATION ${CMAKE_INSTALL_LIBDIR}/cxx/bmi | |
| COMPONENT runtime | |
| ) | |
| endif() | |
| install(TARGETS hpx_${lib} | |
| EXPORT HPXInternalTargets | |
| LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime | |
| ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime | |
| RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime | |
| ${module_installation} | |
| ) | |
| ``` | |
| What this does: | |
| * declares the `.ixx` as a `CXX_MODULES` file set so compilers produce BMIs; | |
| * installs the module interface units and BMIs alongside libraries for consumers. | |
| **b) Generated global header chooses `#include` or `import` at build time** | |
| Essentials only (from `cmake/templates/global_module_header_modules.hpp.in`): | |
| ```cpp | |
| #pragma once | |
| #include <hpx/config/defines.hpp> | |
| #if !defined(HPX_HAVE_CXX_MODULES) || \ | |
| defined(HPX_APPLICATION_DOESNT_USE_CXX_MODULES) || \ | |
| defined(HPX_CORE_EXPORTS) || defined(HPX_FULL_EXPORTS) || \ | |
| defined(HPX_LIBRARY_EXPORTS) || defined(HPX_COMPONENT_EXPORTS) || \ | |
| defined(HPX_BUILD_MODULE) | |
| @module_headers@ // expands to traditional #includes | |
| #else | |
| import HPX.Core; // module path when consumers opt into modules | |
| #endif | |
| ``` | |
| Effect: | |
| * Libraries building HPX itself (or consumers that opt out) keep using `#include`. | |
| * Consumers with modules enabled transparently switch to `import HPX.Core;`. | |
| * Tests and examples are kept untouched and CI passed on both modes | |
| --- | |
| ## Current state | |
| * `HPX.Core` umbrella module (`libs/core/hpx_core.ixx`) added. | |
| * Version API exposed through modules with the new macros. | |
| * Build system produces and installs module interfaces and BMIs. | |
| * Backward compatibility validated: headers continue to work; `import HPX.Core;` also works. | |
| * Tested across major compilers used in CI for the PR. | |
| --- | |
| ## Challenges and lessons | |
| * Backward compatibility drives the shape of both macros and build logic. | |
| * Non-template exports require `export extern "C++"` to yield linkable symbols. | |
| * System headers must stay in the global module fragment to avoid ODR and diagnostic issues. | |
| * Avoiding duplication with the umbrella pattern simplifies maintenance while enabling gradual migration. | |
| --- | |
| ## Next steps | |
| * Continue exposing additional `libs/core` modules using the same pattern. | |
| * Tighten CMake integration so newly exposed modules require minimal boilerplate. | |
| * Measure build and runtime effects of imports vs includes in representative HPX applications. | |
| --- | |
| ## Code and references | |
| * Main work: PR #6761 — includes `hpx_core.ixx`, macro updates in `export_definitions.hpp`, version API changes, and CMake additions. | |
| --- | |
| ## Conclusion | |
| This work establishes a module-aware export scheme, an umbrella interface for `HPX.Core`, and CMake plumbing that installs module interfaces and BMIs. Most importantly, it preserves backward compatibility: existing `#include` code continues to compile unchanged, and users can opt in to `import HPX.Core;`. I will keep extending module coverage across HPX using this approach. | |
| --- | |
| ## Acknowledgments | |
| I would like to thank my mentors, Dr. Hartmut Kaiser and Panos Papadopoulos, for their guidance and feedback throughout the project. Their expertise in both C++ and HPX design decisions was crucial for navigating the challenges of symbol export, module boundaries, and build system integration. I also thank the STE||AR Group community for code reviews, discussions, and support during GSoC. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment