Skip to content

Conversation

@MilanBorovy
Copy link

@MilanBorovy MilanBorovy commented Oct 17, 2025

  • Make EnTT consumable as C++ module
  • Added ENTT_MODULE CMake option enabling EnTT compilation as C++ module
  • Added ENTT_USER_CONFIG CMake option to be able to specify C++ config defines

I've noticed that similar pull rq is already ongoing but seems to be dead for a few months.

Instead of exporting symbols in separate files, export macros are used (similar to how fmt does it).

By setting ENTT_MODULE CMake option to ON the EnTT::EnTT target becomes module library allowing EnTT to be consumed as import entt;.

Another option ENTT_USER_CONFIG is added to CMake. It can be used to configure entt as on can't simply #define all configurations inside source with modules.

Usage example:

entt_config.hpp

#define ENTT_ID_TYPE std::uint64_t
#define ENTT_USE_ATOMIC

CMakeLists.txt

...
set(ENTT_MODULE ON)
cmake_path(APPEND PROJECT_SOURCE_DIR user_config.hpp OUTPUT_VARIABLE ENTT_USER_CONFIG)

add_subdirectory(entt)

...

target_link_libraries(exe PUBLIC EnTT::EnTT)

Some explanation of this would be nice to put in wiki, unfortunately I can't pull request changes to wiki AFAIK.

* Make EnTT consumable as C++ module
* Added `ENTT_MODULE` CMake option enabling EnTT compilation as C++ module
* Added `ENTT_USER_CONFIG` CMake option to be able to specify C++ config defines
@MilanBorovy MilanBorovy marked this pull request as draft October 17, 2025 02:20
@MilanBorovy MilanBorovy marked this pull request as ready for review October 17, 2025 02:31
@MilanBorovy MilanBorovy marked this pull request as draft October 17, 2025 08:13
@MilanBorovy
Copy link
Author

I've added free operators into exports as unlike member operators they're not reachable just by aliasing the according class.

Also declaring unqualified friend class results in forced injection under modules resulting in ambiguous symbols later. I've made sure that all friend class declarations uses qualified names to avoid that.

@MilanBorovy MilanBorovy marked this pull request as ready for review October 22, 2025 22:22
@skypjack skypjack self-requested a review October 23, 2025 17:01
@skypjack skypjack self-assigned this Oct 23, 2025
@skypjack
Copy link
Owner

This looks interesting. Quite a lot actually. Thanks for contributing!
Can I ask you why this solution should be better than the one in the other PR? Don't get me wrong, I'm not saying it isn't. I'm just trying to understand pros and cons. Try explaining this to me like I'm 3 years old. 🙂
I would like to involve @elvisdukaj who implemented the first draft. Do you see any issues with this approach? You've done more research on the topic than I have, so your opinion might be helpful.

@skypjack skypjack added help wanted I don't have the knowledge/resources to do that feature request feature request not yet accepted labels Oct 23, 2025
@theoparis
Copy link

theoparis commented Oct 23, 2025

I was going to test this PR myself but I noticed that when -DENTT_MODULE=ON it causes an error when -DENTT_INSTALL=ON is also set.

CMake Error at CMakeLists.txt:307 (install):
  install TARGETS target EnTT is exported but not all of its interface file
  sets are installed


CMake Error at CMakeLists.txt:327 (export):
  export Export set "EnTTTargets" not found.

@MilanBorovy
Copy link
Author

@skypjack Hi, I see several benefits in this approach compared to the other one.

I'll start right away with the one I see as the most important. There is an important difference between where all the definitions are located.

In the other PR all the symbols are defined inside global module fragment (GMF 10.4). This fragment is meant for preprocessor directives, including includes of legacy code. When the module is built all symbols that are not decl-reachable (10.4.3) are dumped. This might lead to a situation described in the 2nd example (10.4.6), where a such defined symbol, might be "incorrectly" marked as unreachable and thus dumped away, resulting in compilation error. Due to this I'm highly concerned about entt::internal definitions, that might get dumped that way.

You can see similar behavior even with STL being used in entt. When using import entt you might be forced to use also import std/#include <put-your-favorite-stl-lib-here> even though not using it directly, but through entt. But unlike with STL that you might include separately from entt, you can't simply #include <entt/entt.hpp> along with import entt just to get to the internal symbols.

It doesn't mean this approach is bad, for me it just seems more challenging to maintain (and thus error-prone) as you have to follow extra set of rules to ensure all symbols are decl-reachable.

In this PR, all the entt symbols are defined in the module itself and are not dumped by the compiler the same way. All the intenal symbols are still reachable (but not visible) for the caller without the need to include the internals in another way.

Another benefit I see is potentially less boilerplate (but that's somewhat questionable) as you don't need to re-export all the symbols again as in .inc files in the other PR.

On the other hand, with the other approach you have nice concise list of all exported symbols at hand, but you have to manage it more carefully (when adding new symbol you need to think about adding it to an extra file).

Also with the other PR's approach you don't have to care about dependencies again. As you just include the entt/entt.hpp which already has its dependencies sorted, you then include the .inc files in any order you want (let's say alphabetically). With this PR's approach as you can see in entt.ixx the dependecies needs to be resolved for the second time and includes cannot be reordered arbitrarily.

Also all the includes inside all the source files has to be suppressed to avoid linking unwanted symbols (like STL ones) to the module (as it will result in symbol ambiguity if STL and entt is included in some project). Technically, one can suppress only non-entt headers (+ few of the config headers) in modules and it might be possible to also include just entt/entt.hpp, might be worth the investigation.

And last thing to mention is that the GMF approach is meant mostly for the transition to modules and is more fit to wrap external library if one wants to use it in own project and doesn't have much control over the library's source. For the actual implementation the symbol definitions directly inside module seems more fit.

I would really love to hear @elvisdukaj's opinion as I'm still not absolutely sure about everything personally. Even though modules are there for a few years already, they're still kinda new and niche and hard to put into existing codebases at work so hard to get to first hand experience with them still :)

@MilanBorovy
Copy link
Author

@theoparis installation should be fixed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request feature request not yet accepted help wanted I don't have the knowledge/resources to do that

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants