Add entt as C++ module#1290
Conversation
|
I've added free operators into exports as unlike member operators they're not reachable just by aliasing the according class. Also declaring unqualified |
|
This looks interesting. Quite a lot actually. Thanks for contributing! |
|
I was going to test this PR myself but I noticed that when |
|
@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 You can see similar behavior even with STL being used in entt. When using 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 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 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 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 :) |
|
@theoparis installation should be fixed now. |
|
Hi! Thanks for keeping me in the loop. @MilanBorovy great work — I really like your approach, and I’m aligned with your points. When I started my PR, my goal was to wrap EnTT so it could be consumed as a module by my project, so I went with GMF. I used the glm module file as a starting point. At the time, I didn’t consider the decl-reachability issue because the library lived inside my source tree. In that regard, your approach looks more robust and less error-prone. I still have two reservations: Ergonomics around configuration. Requiring File extensions for C++ modules. I’d prefer avoiding a new extension just for modules and sticking with *.cpp. Different compilers assign special meanings to various extensions (e.g., MSVC treats Overall, I think is a more maintainable approach in the long term. |
|
Ok, sounds reaonsable. |
|
ad 1) I'm not sure wdym by the or Unfortunately the first one cannot be done using modules and the second one I've implemented by the If there's any other config option please enlighten me and I may try to make it work with it. Anyway, I agree that this must be documented, but as I've stated in the PR I cannot include wiki changes in PR AFAIK and will need @skypjack to document it, I guess. ad 2) I understand your point and kind of agree with that, as module should be kind of a new standard for C++ so why to treat it differently from other implementation files. I just remember VS/MSVC being pretty aggressive about enforcing use of I'm not sure if this still applies (hopefully not) and would gladly change the extension to |
|
@skypjack |
|
@theoparis |
You can actually 🙂 the wiki is part of this repo too, see the |
* 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 added documentation of the module configuration. Also rebased to the current version of master. |
| #include <set> | ||
| #include <string_view> | ||
| #include <unordered_set> | ||
| #include <utility> |
There was a problem hiding this comment.
MSVC 19.50.35719.0 failed to build module with following errors
entt\src\entt\meta/meta.hpp(1425): error C2027: use of undefined type 'std::array<entt::meta_any,0>'
entt\src\entt\graph/dot.hpp(27): error C2676: binary '<<': 'std::ostream' does not define this operator or a conversion to a type acceptable to the predefined operator
This fixes it for me
| #include <utility> | |
| #include <utility> | |
| #include <array> | |
| #include <ostream> |
There was a problem hiding this comment.
Thank you. I've added both headers to the module file.
|
I tried to use your branch, ran into these errors Not sure if this is expected or if I've messed something up. I consumed the library like so. set(ENTT_MODULE ON CACHE BOOL "ENTT MODULE" FORCE)
FetchContent_Declare(
entt_fetch
GIT_REPOSITORY https://github.com/MilanBorovy/entt.git
GIT_TAG 6657430
)
FetchContent_MakeAvailable(vuk_fetch glfw_fetch glm_fetch entt_fetch)
target_link_libraries(ris_library
PUBLIC
glfw
vuk
vk-bootstrap
glm
slang
shaderc_shared
glslang::glslang
SPIRV-Tools
SPIRV-Tools-opt
EnTT::EnTT
) |
@MilanBorovy I think it's enough to rebase on -- Let me know if you can do that, and also fix the conflicts. |
|
Hi, I most likely won't be able to do that until the end of the year. I expect to be available in early January. So depends how fast you want it done. 😅 |
Considering that I just got back from vacation today, I think this answers your question! 😅 |
|
I tried out this branch with multiple compilers and had to reorder the include order in the module interface to this: module;
#define ENTT_MODULE
#define ENTT_MODULE_EXPORT export
#define ENTT_MODULE_EXPORT_BEGIN export {
#define ENTT_MODULE_EXPORT_END }
#include <algorithm>
#include <array>
#include <cmath>
#include <deque>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <string_view>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>
#ifdef ENTT_USER_CONFIG
# include ENTT_USER_CONFIG
#endif // ENTT_USER_CONFIG
#include "config/config.h"
#include "config/macro.h"
#include "config/version.h"
#include "core/attribute.h"
export module entt;
#include "core/fwd.hpp"
#include "core/utility.hpp"
#include "core/hashed_string.hpp"
#include "core/type_traits.hpp"
#include "core/type_info.hpp"
#include "core/algorithm.hpp"
#include "core/any.hpp"
#include "core/bit.hpp"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
#include "core/ident.hpp"
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/ranges.hpp"
#include "core/tuple.hpp"
#include "container/fwd.hpp"
#include "container/dense_map.hpp"
#include "container/dense_set.hpp"
#include "container/table.hpp"
#include "signal/fwd.hpp"
#include "signal/delegate.hpp"
#include "signal/dispatcher.hpp"
#include "signal/emitter.hpp"
#include "signal/sigh.hpp"
#include "graph/fwd.hpp"
#include "graph/adjacency_matrix.hpp"
#include "graph/dot.hpp"
#include "graph/flow.hpp"
#include "entity/fwd.hpp"
#include "entity/component.hpp"
#include "entity/entity.hpp"
#include "entity/group.hpp"
#include "entity/handle.hpp"
#include "entity/helper.hpp"
#include "entity/mixin.hpp"
#include "entity/organizer.hpp"
#include "entity/ranges.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
#include "entity/view.hpp"
#include "locator/locator.hpp"
#include "meta/fwd.hpp"
#include "meta/type_traits.hpp"
#include "meta/adl_pointer.hpp"
#include "meta/pointer.hpp"
#include "meta/policy.hpp"
#include "meta/context.hpp"
#include "meta/range.hpp"
#include "meta/template.hpp"
#include "meta/node.hpp"
#include "meta/meta.hpp"
#include "meta/container.hpp"
#include "meta/resolve.hpp"
#include "meta/utility.hpp"
#include "meta/factory.hpp"
#include "poly/fwd.hpp"
#include "poly/poly.hpp"
#include "process/fwd.hpp"
#include "process/process.hpp"
#include "process/scheduler.hpp"
#include "resource/fwd.hpp"
#include "resource/cache.hpp"
#include "resource/loader.hpp"
#include "resource/resource.hpp"With the emscripten compiler I also had to export the forward declarations in node.hpp, because otherwise I got the following error ENTT_MODULE_EXPORT_BEGIN
class meta_any;
class meta_type;
class meta_handle;
ENTT_MODULE_EXPORT_ENDWith these changes it compiles with MSVC 17.4, GCC 15.2 and emsdk 4.0.23. |
|
I'm having a lot of compilation errors, because header includes inside the module interface unit seemingly are placed in incorrect order. This is the complete reordered list (complete ixx file), so that the module compiles: NOTEI cloned from I'm using MSVC:
Complete fixed ixx file belowmodule;
#define ENTT_MODULE
#define ENTT_MODULE_EXPORT export
#define ENTT_MODULE_EXPORT_BEGIN export {
#define ENTT_MODULE_EXPORT_END }
#include <algorithm>
#include <array>
#include <cmath>
#include <deque>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <string_view>
#include <unordered_set>
#include <utility>
#include <typeinfo>
#ifdef ENTT_USER_CONFIG
# include ENTT_USER_CONFIG
#endif // ENTT_USER_CONFIG
#include "config/config.h"
#include "config/macro.h"
#include "config/version.h"
#include "core/attribute.h"
export module entt;
#include "core/algorithm.hpp"
#include "core/fwd.hpp"
#include "core/type_traits.hpp"
#include "core/any.hpp"
#include "core/bit.hpp"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
#include "core/hashed_string.hpp"
#include "core/ident.hpp"
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/ranges.hpp"
#include "core/tuple.hpp"
#include "core/type_info.hpp"
#include "core/utility.hpp"
#include "container/dense_map.hpp"
#include "container/dense_set.hpp"
#include "container/fwd.hpp"
#include "container/table.hpp"
#include "signal/fwd.hpp"
#include "signal/delegate.hpp"
#include "signal/dispatcher.hpp"
#include "signal/emitter.hpp"
#include "signal/sigh.hpp"
#include "graph/fwd.hpp"
#include "graph/adjacency_matrix.hpp"
#include "graph/dot.hpp"
#include "graph/flow.hpp"
#include "entity/component.hpp"
#include "entity/entity.hpp"
#include "entity/fwd.hpp"
#include "entity/group.hpp"
#include "entity/handle.hpp"
#include "entity/helper.hpp"
#include "entity/mixin.hpp"
#include "entity/organizer.hpp"
#include "entity/ranges.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
#include "entity/view.hpp"
#include "locator/locator.hpp"
#include "meta/adl_pointer.hpp"
#include "meta/fwd.hpp"
#include "meta/context.hpp"
#include "meta/type_traits.hpp"
#include "meta/node.hpp"
#include "meta/range.hpp"
#include "meta/meta.hpp"
#include "meta/resolve.hpp"
#include "meta/container.hpp"
#include "meta/policy.hpp"
#include "meta/utility.hpp"
#include "meta/factory.hpp"
#include "meta/pointer.hpp"
#include "meta/template.hpp"
#include "poly/fwd.hpp"
#include "poly/poly.hpp"
#include "process/fwd.hpp"
#include "process/process.hpp"
#include "process/scheduler.hpp"
#include "resource/resource.hpp"
#include "resource/cache.hpp"
#include "resource/fwd.hpp"
#include "resource/loader.hpp" |
ENTT_MODULECMake option enabling EnTT compilation as C++ moduleENTT_USER_CONFIGCMake option to be able to specify C++ config definesI'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_MODULECMakeoption toONtheEnTT::EnTTtarget becomes module library allowingEnTTto be consumed asimport entt;.Another option
ENTT_USER_CONFIGis added toCMake. It can be used to configure entt as on can't simply #define all configurations inside source with modules.Usage example:
entt_config.hpp
CMakeLists.txt
Some explanation of this would be nice to put in wiki, unfortunately I can't pull request changes to wiki AFAIK.