updated doc
This commit is contained in:
2
TODO
2
TODO
@@ -23,8 +23,6 @@
|
||||
* cleanup - see https://github.com/skypjack/entt/commit/ad5cedc08c83e8cbcc8aaeac9634d44624ffe35a#commitcomment-32380903
|
||||
|
||||
==> can we do more for shared libraries? who knows... see #144
|
||||
* get rid of get_pool_data (welcome structured bindings)
|
||||
* to be updated: dispatcher
|
||||
* to be updated: registry
|
||||
* to be updated: emitter
|
||||
* to be updated: doc
|
||||
|
||||
@@ -27,12 +27,12 @@ add_custom_target(
|
||||
CONTRIBUTING.md
|
||||
core.md
|
||||
entity.md
|
||||
lib.md
|
||||
links.md
|
||||
locator.md
|
||||
meta.md
|
||||
process.md
|
||||
resource.md
|
||||
shared.md
|
||||
signal.md
|
||||
extra.dox
|
||||
)
|
||||
|
||||
141
docs/lib.md
Normal file
141
docs/lib.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Push EnTT across boundaries
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Shared types and traits class](#shared-types-and-traits-class)
|
||||
* [Do not mix types](#do-not-mix-types)
|
||||
* [Macros, macros everywhere](#macros-macros-everywhere)
|
||||
* [Conflicts](#conflict)
|
||||
* [Note](#note)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` has historically had a limit when used across boundaries on Windows in
|
||||
general and on GNU/Linux when default visibility was set to _hidden_. The
|
||||
limitation is due mainly to a custom utility used to assign unique, sequential
|
||||
identifiers to different types. Unfortunately, this tool is used by several core
|
||||
classes (the `registry` among the others) that are thus almost unusable across
|
||||
boundaries.<br/>
|
||||
The reasons for that are beyond the purposes of this document. However, the good
|
||||
news is that `EnTT` also offers now a way to overcome this limit and to push
|
||||
things across boundaries without problems when needed.
|
||||
|
||||
# Shared types and traits class
|
||||
|
||||
To allow a type to work properly across boundaries when used by a class that
|
||||
requires to assign unique identifiers to types, users must specialize a class
|
||||
template to literally give a compile-time name to the type itself.<br/>
|
||||
The name of the class template is `shared_traits` and the specialization must be
|
||||
such that it exposes a static constexpr data member named `value` having type
|
||||
either `ENTT_ID_TYPE` or `entt::hashed_string::hash_type`. Its value is the user
|
||||
defined unique identifier assigned to the specific type.<br/>
|
||||
Identifiers are not to be sequentially generated in this case.
|
||||
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
struct my_type { /* ... */ };
|
||||
|
||||
template<>
|
||||
struct entt::shared_traits<my_type> {
|
||||
static constexpr auto value = "my_type"_hs;
|
||||
};
|
||||
```
|
||||
|
||||
Because of the rules of the language, the specialization must reside in the
|
||||
global namespace or in the `entt` namespace. There is no way to change this rule
|
||||
unfortunately, because it doesn't depend on the library itself.
|
||||
|
||||
The good aspect of this approach is that it's not intrusive at all. The other
|
||||
way around was in fact forcing users to inherit all their classes from a common
|
||||
base. Something to avoid, at least from my point of view.<br/>
|
||||
However, despite the fact that it's not intrusive, it would be great if it was
|
||||
also easier to use and a bit less error-prone. This is why a bunch of macros
|
||||
exist to ease defining shared types.
|
||||
|
||||
## Do not mix types
|
||||
|
||||
Someone might think that this trick is valid only for the types to push across
|
||||
boundaries. This isn't how things work. In fact, the problem is more complex
|
||||
than that.<br/>
|
||||
As a rule of thumb, users should never mix shared and non-shared types. Whenever
|
||||
a type is made shareable, all the types should be made shareable. As an example,
|
||||
consider the `registry` class template. In case it is pushed across boundaries,
|
||||
all the types of components should be shareable to avoid subtle bugs.
|
||||
|
||||
Indeed, this constraint can be relaxed in many cases. However, it is difficult
|
||||
to define a general rule to follow that is not the most stringent, unless users
|
||||
know exactly what they are doing. Therefore, I won't elaborate on giving further
|
||||
details on the topic.
|
||||
|
||||
# Macros, macros everywhere
|
||||
|
||||
The library comes with a set of predefined macros to use to declare shared types
|
||||
or export already existing ones. In particular:
|
||||
|
||||
* `ENTT_SHARED_TYPE` can be used to make shareable already existing types. This
|
||||
macro must be used in the global namespace even when types to make shareable
|
||||
are not.
|
||||
|
||||
```cpp
|
||||
ENTT_SHARED_TYPE(my_type)
|
||||
ENTT_SHARED_TYPE(ns::another_type)
|
||||
```
|
||||
|
||||
* `ENTT_SHARED_STRUCT` can be used to define and export a struct at the same
|
||||
time. It accepts also an optional namespace in which to define the given type.
|
||||
This macro must be used in the global namespace.
|
||||
|
||||
```cpp
|
||||
ENTT_SHARED_STRUCT(my_type, { /* struct definition */})
|
||||
ENTT_SHARED_STRUCT(ns, another_type, { /* struct definition */})
|
||||
```
|
||||
|
||||
* `ENTT_SHARED_CLASS` can be used to define and export a class at the same
|
||||
time. It accepts also an optional namespace in which to define the given type.
|
||||
This macro must be used in the global namespace.
|
||||
|
||||
```cpp
|
||||
ENTT_SHARED_CLASS(my_type, { /* class definition */})
|
||||
ENTT_SHARED_CLASS(ns, another_type, { /* class definition */})
|
||||
```
|
||||
|
||||
Nested namespaces are supported out of the box as well in all cases. As an
|
||||
example:
|
||||
|
||||
```cpp
|
||||
ENTT_SHARED_STRUCT(nested::ns, my_type, { /* struct definition */})
|
||||
```
|
||||
|
||||
These macros can be used to avoid specializing the `shared_traits` class
|
||||
template. In all cases, the name of the class is used also as a seed to generate
|
||||
the compile-time unique identifier.
|
||||
|
||||
## Conflicts
|
||||
|
||||
When using macros, unique identifiers are 32/64 bit integers generated by
|
||||
hashing strings during compilation. Therefore, conflicts are rare but still
|
||||
possible. In case of conflicts, everything simply will get broken at runtime and
|
||||
the strangest things will probably take place.<br/>
|
||||
Unfortunately, there is no safe way to prevent it. If this happens, it will be
|
||||
enough to give a different name to one of the conflicting types to solve the
|
||||
problem. To do this, users can either assign a different name to the class or
|
||||
define directly a specialization for the `shared_traits` class template.
|
||||
|
||||
# Note
|
||||
|
||||
I'm still working hard to make everything work across boundaries.<br/>
|
||||
The classes affected by the problem were `registry`, `dispatcher` and `emitter`.
|
||||
Currently, only the `registry` class fully support _shared types_. Using
|
||||
`dispatcher` and `emitter` across boundaries isn't allowed yet and can result in
|
||||
unexpected behavior on Windows in general and on GNU/Linux when default
|
||||
visibility is set to _hidden_.
|
||||
|
||||
Stay tuned for future updates.
|
||||
@@ -1,37 +0,0 @@
|
||||
### EnTT and shared libraries
|
||||
|
||||
To make sure that an application and a shared library that use both `EnTT` can
|
||||
interact correctly when symbols are hidden by default, there are some tricks to
|
||||
follow.<br/>
|
||||
In particular and in order to avoid undefined behaviors, all the instantiation
|
||||
of the `family` class template shall be made explicit along with the system-wide
|
||||
specifier to use to export them.
|
||||
|
||||
At the time I'm writing this document, the classes that use internally the above
|
||||
mentioned class template are `dispatcher`, `emitter` and `registry`. Therefore
|
||||
and as an example, if you use the `registry` class template in your shared
|
||||
library and want to set symbols visibility to _hidden_ by default, the following
|
||||
lines are required to allow it to function properly with a client that also uses
|
||||
the `registry` somehow:
|
||||
|
||||
* On GNU/Linux:
|
||||
|
||||
```cpp
|
||||
namespace entt {
|
||||
template class __attribute__((visibility("default"))) family<struct internal_registry_component_family>;
|
||||
template class __attribute__((visibility("default"))) family<struct internal_registry_handler_family>;
|
||||
}
|
||||
```
|
||||
|
||||
* On Windows:
|
||||
|
||||
```cpp
|
||||
namespace entt {
|
||||
template class __declspec(dllexport) family<struct internal_registry_component_family>;
|
||||
template class __declspec(dllexport) family<struct internal_registry_handler_family>;
|
||||
}
|
||||
```
|
||||
|
||||
Otherwise, the risk is that type identifiers are different between the shared
|
||||
library and the application and this will prevent the whole thing from
|
||||
functioning correctly for obvious reasons.
|
||||
Reference in New Issue
Block a user