EnTT  3.2.0
docs/md/lib.md
1 # Push EnTT across boundaries
2 
3 <!--
4 @cond TURN_OFF_DOXYGEN
5 -->
6 # Table of Contents
7 
8 * [Introduction](#introduction)
9 * [Named types and traits class](#named-types-and-traits-class)
10  * [Do not mix types](#do-not-mix-types)
11 * [Macros, macros everywhere](#macros-macros-everywhere)
12  * [Conflicts](#conflicts)
13 * [Runtime reflection system](#runtime-reflection-system)
14 * [Allocations: the dark side of the force](#allocations-the-dark-side-of-the-force)
15 <!--
16 @endcond TURN_OFF_DOXYGEN
17 -->
18 
19 # Introduction
20 
21 `EnTT` has historically had a limit when used across boundaries on Windows in
22 general and on GNU/Linux when default visibility was set to _hidden_. The
23 limitation is due mainly to a custom utility used to assign unique, sequential
24 identifiers to different types. Unfortunately, this tool is used by several core
25 classes (the `registry` among the others) that are thus almost unusable across
26 boundaries.<br/>
27 The reasons for that are beyond the purposes of this document. However, the good
28 news is that `EnTT` also offers now a way to overcome this limit and to push
29 things across boundaries without problems when needed.
30 
31 # Named types and traits class
32 
33 To allow a type to work properly across boundaries when used by a class that
34 requires to assign unique identifiers to types, users must specialize a class
35 template to literally give a compile-time name to the type itself.<br/>
36 The name of the class template is `name_type_traits` and the specialization must
37 be such that it exposes a static constexpr data member named `value` having type
38 either `ENTT_ID_TYPE` or `entt::hashed_string::hash_type`. Its value is the user
39 defined unique identifier assigned to the specific type.<br/>
40 Identifiers are not to be sequentially generated in this case.
41 
42 As an example:
43 
44 ```cpp
45 struct my_type { /* ... */ };
46 
47 template<>
48 struct entt::named_type_traits<my_type> {
49  static constexpr auto value = "my_type"_hs;
50 };
51 ```
52 
53 Because of the rules of the language, the specialization must reside in the
54 global namespace or in the `entt` namespace. There is no way to change this rule
55 unfortunately, because it doesn't depend on the library itself.
56 
57 The good aspect of this approach is that it's not intrusive at all. The other
58 way around was in fact forcing users to inherit all their classes from a common
59 base. Something to avoid, at least from my point of view.<br/>
60 However, despite the fact that it's not intrusive, it would be great if it was
61 also easier to use and a bit less error-prone. This is why a bunch of macros
62 exist to ease defining named types.
63 
64 ## Do not mix types
65 
66 Someone might think that this trick is valid only for the types to push across
67 boundaries. This isn't how things work. In fact, the problem is more complex
68 than that.<br/>
69 As a rule of thumb, users should never mix named and non-named types. Whenever
70 a type is given a name, all the types must be given a name. As an example,
71 consider the `registry` class template: in case it is pushed across boundaries,
72 all the types of components should be assigned a name to avoid subtle bugs.
73 
74 Indeed, this constraint can be relaxed in many cases. However, it is difficult
75 to define a general rule to follow that is not the most stringent, unless users
76 know exactly what they are doing. Therefore, I won't elaborate on giving further
77 details on the topic.
78 
79 # Macros, macros everywhere
80 
81 The library comes with a set of predefined macros to use to declare named types
82 or export already existing ones. In particular:
83 
84 * `ENTT_NAMED_TYPE` can be used to assign a name to already existing types. This
85  macro must be used in the global namespace even when the types to be named are
86  not.
87 
88  ```cpp
89  ENTT_NAMED_TYPE(my_type)
90  ENTT_NAMED_TYPE(ns::another_type)
91  ```
92 
93 * `ENTT_NAMED_STRUCT` can be used to define and export a struct at the same
94  time. It accepts also an optional namespace in which to define the given type.
95  This macro must be used in the global namespace.
96 
97  ```cpp
98  ENTT_NAMED_STRUCT(my_type, { /* struct definition */})
99  ENTT_NAMED_STRUCT(ns, another_type, { /* struct definition */})
100  ```
101 
102 * `ENTT_NAMED_CLASS` can be used to define and export a class at the same
103  time. It accepts also an optional namespace in which to define the given type.
104  This macro must be used in the global namespace.
105 
106  ```cpp
107  ENTT_NAMED_CLASS(my_type, { /* class definition */})
108  ENTT_NAMED_CLASS(ns, another_type, { /* class definition */})
109  ```
110 
111 Nested namespaces are supported out of the box as well in all cases. As an
112 example:
113 
114 ```cpp
115 ENTT_NAMED_STRUCT(nested::ns, my_type, { /* struct definition */})
116 ```
117 
118 These macros can be used to avoid specializing the `named_type_traits` class
119 template. In all cases, the name of the class is used also as a seed to generate
120 the compile-time unique identifier.
121 
122 ## Conflicts
123 
124 When using macros, unique identifiers are 32/64 bit integers generated by
125 hashing strings during compilation. Therefore, conflicts are rare but still
126 possible. In case of conflicts, everything simply will get broken at runtime and
127 the strangest things will probably take place.<br/>
128 Unfortunately, there is no safe way to prevent it. If this happens, it will be
129 enough to give a different value to one of the conflicting types to solve the
130 problem. To do this, users can either assign a different name to the class or
131 directly define a specialization for the `named_type_traits` class template.
132 
133 # Runtime reflection system
134 
135 The runtime reflection system deserves a special mention when it comes to using
136 it across boundaries.<br/>
137 As in all other cases, it's necessary to give a name to the types. However, this
138 time this isn't enough to get the job done.
139 
140 The runtime reflection system is linked to a static context to which the visible
141 components are linked. A component is visible when it's given a name, so the
142 named components are implicitly visible.<br/>
143 Different contexts don't relate to each other. Therefore, to allow the use of
144 named types across boundaries, a context must also be shared.
145 
146 Sharing a context is straightforward. First of all, the local one must be
147 acquired:
148 
149 ```
150 entt::meta_ctx ctx{};
151 ```
152 
153 Then, it must be pushed across boundaries and the receiving space must set it as
154 its global context, thus releasing the local one that remains available but is
155 no longer referred to by the runtime reflection system:
156 
157 ```
158 entt::meta_ctx::bind(ctx);
159 ```
160 
161 From now on, both spaces will refer to the same context and on it will be
162 associated the new visible meta types, no matter _where_ they are created.
163 
164 A context can also be reset and then associated again locally as:
165 
166 ```
167 entt::meta_ctx::bind{entt::meta_ctx{});
168 ```
169 
170 This is allowed because local and global contexts are separated. Therefore, it's
171 always possible to make the local context the current one again.
172 
173 # Allocations: the dark side of the force
174 
175 As long as `EnTT` won't support custom allocators, another problem with
176 allocations will remain alive instead. This is in fact easily solved, or at
177 least it is if one knows it.
178 
179 To allow users to add types dynamically, the library makes extensive use of type
180 erasure techniques and dynamic allocations for pools (whether they are for
181 components, events or anything else). The problem occurs when, for example, a
182 registry is created on one side of a boundary and a pool is dynamically created
183 on the other side. In the best case, everything will crash at the exit, while at
184 worst it will do so at runtime.<br/>
185 To avoid problems, the pools must be generated from the same side of the
186 boundary where the object that owns them is also created. As an example, when
187 the registry is created in the main executable and used across boundaries for a
188 given type of component, the pool for that type must be created before passing
189 around the registry itself. To do this is fortunately quite easy, since it is
190 sufficient to invoke any of the methods that involve the given type (continuing
191 the example with the registry, a call to `reserve` or `size` is more than
192 enough).
193 
194 Maybe one day some dedicated methods will be added that do nothing but create a
195 pool for a given type. Until now it has been preferred to keep the API cleaner
196 as they are not strictly necessary.