Squashed 'external/entt/entt/' content from commit fef92113

git-subtree-dir: external/entt/entt
git-subtree-split: fef921132cae7588213d0f9bcd2fb9c8ffd8b7fc
This commit is contained in:
2023-07-25 11:29:51 +02:00
commit 5c7231b7a3
242 changed files with 146004 additions and 0 deletions

121
docs/md/config.md Normal file
View File

@ -0,0 +1,121 @@
# Crash Course: configuration
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Definitions](#definitions)
* [ENTT_NOEXCEPTION](#entt_noexception)
* [ENTT_USE_ATOMIC](#entt_use_atomic)
* [ENTT_ID_TYPE](#entt_id_type)
* [ENTT_SPARSE_PAGE](#entt_sparse_page)
* [ENTT_PACKED_PAGE](#entt_packed_page)
* [ENTT_ASSERT](#entt_assert)
* [ENTT_ASSERT_CONSTEXPR](#entt_assert_constexpr)
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` has become almost completely customizable over time, in many
respects. These variables are just one of the many ways to customize how it
works.<br/>
In the vast majority of cases, users will have no interest in changing the
default parameters. For all other cases, the list of possible configurations
with which it's possible to adjust the behavior of the library at runtime can be
found below.
# Definitions
All options are intended as parameters to the compiler (or user-defined macros
within the compilation units, if preferred).<br/>
Each parameter can result in internal library definitions. It's not recommended
to try to also modify these definitions, since there is no guarantee that they
will remain stable over time unlike the options below.
## ENTT_NOEXCEPTION
Define this variable without assigning any value to it to turn off exception
handling in `EnTT`.<br/>
This is roughly equivalent to setting the compiler flag `-fno-exceptions` but is
also limited to this library only.
## ENTT_USE_ATOMIC
In general, `EnTT` doesn't offer primitives to support multi-threading. Many of
the features can be split over multiple threads without any explicit control and
the user is the one who knows if a synchronization point is required.<br/>
However, some features aren't easily accessible to users and are made
thread-safe by means of this definition.
## ENTT_ID_TYPE
`entt::id_type` is directly controlled by this definition and widely used within
the library.<br/>
By default, its type is `std::uint32_t`. However, users can define a different
default type if necessary.
## ENTT_SPARSE_PAGE
It's known that the ECS module of `EnTT` is based on _sparse sets_. What is less
known perhaps is that the sparse arrays are paged to reduce memory usage.<br/>
Default size of pages (that is, the number of elements they contain) is 4096 but
users can adjust it if appropriate. In all case, the chosen value **must** be a
power of 2.
## ENTT_PACKED_PAGE
As it happens with sparse arrays, packed arrays are also paginated. However, in
this case the aim isn't to reduce memory usage but to have pointer stability
upon component creation.<br/>
Default size of pages (that is, the number of elements they contain) is 1024 but
users can adjust it if appropriate. In all case, the chosen value **must** be a
power of 2.
## ENTT_ASSERT
For performance reasons, `EnTT` doesn't use exceptions or any other control
structures. In fact, it offers many features that result in undefined behavior
if not used correctly.<br/>
To get around this, the library relies on a lot of asserts for the purpose of
detecting errors in debug builds. By default, it uses `assert` internally. Users
are allowed to overwrite its behavior by setting this variable.
### ENTT_ASSERT_CONSTEXPR
Usually, an assert within a `constexpr` function isn't a big deal. However, in
case of extreme customizations, it might be useful to differentiate.<br/>
For this purpose, `EnTT` introduces an admittedly badly named variable to make
the job easier in this regard. By default, this variable forwards its arguments
to `ENTT_ASSERT`.
### ENTT_DISABLE_ASSERT
Assertions may in turn affect performance to an extent when enabled. Whether
`ENTT_ASSERT` and `ENTT_ASSERT_CONSTEXPR` are redefined or not, all asserts can
be disabled at once by means of this definition.<br/>
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of the
other variables and is therefore meant to disable all controls no matter what.
## ENTT_NO_ETO
In order to reduce memory consumption and increase performance, empty types are
never instantiated nor stored by the ECS module of `EnTT`.<br/>
Use this variable to treat these types like all others and therefore to create a
dedicated storage for them.
## ENTT_STANDARD_CPP
`EnTT` mixes non-standard language features with others that are perfectly
compliant to offer some of its functionalities.<br/>
This definition prevents the library from using non-standard techniques, that
is, functionalities that aren't fully compliant with the standard C++.<br/>
While there are no known portability issues at the time of this writing, this
should make the library fully portable anyway if needed.

67
docs/md/container.md Normal file
View File

@ -0,0 +1,67 @@
# Crash Course: containers
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Containers](#containers)
* [Dense map](#dense-map)
* [Dense set](#dense-set)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
The standard C++ library offers a wide range of containers and it's really
difficult to do better (although it's very easy to do worse, as many examples
available online demonstrate).<br/>
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
the opposite, given the widespread use that is made of standard containers.<br/>
However, the library also tries to fill a gap in features and functionality by
making available some containers initially developed for internal use.
This section of the library is likely to grow larger over time. However, for the
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
For all containers made available, full test coverage and stability over time is
guaranteed as usual.
# Containers
## Dense map
The dense map made available in `EnTT` is a hash map that aims to return a
packed array of elements, so as to reduce the number of jumps in memory during
iterations.<br/>
The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is very close to its counterpart in the standard library, that is,
`std::unordered_map`.<br/>
However, both local and non-local iterators returned by a dense map belong to
the input iterator category although they respectively model the concepts of a
_forward iterator_ type and a _random access iterator_ type.<br/>
This is because they return a pair of references rather than a reference to a
pair. In other words, dense maps return a so called _proxy iterator_ the value
type of which is:
* `std::pair<const Key &, Type &>` for non-const iterator types.
* `std::pair<const Key &, const Type &>` for const iterator types.
This is quite different from what any standard library map returns and should be
taken into account when looking for a drop-in replacement.
## Dense set
The dense set made available in `EnTT` is a hash set that aims to return a
packed array of elements, so as to reduce the number of jumps in memory during
iterations.<br/>
The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is in all respects similar to its counterpart in the standard
library, that is, `std::unordered_set`.<br/>
Therefore, there is no need to go into the API description.

1011
docs/md/core.md Normal file

File diff suppressed because it is too large Load Diff

2252
docs/md/entity.md Normal file

File diff suppressed because it is too large Load Diff

215
docs/md/faq.md Normal file
View File

@ -0,0 +1,215 @@
# Frequently Asked Questions
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [FAQ](#faq)
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
This is a constantly updated section where I'm trying to put the answers to the
most frequently asked questions.<br/>
If you don't find your answer here, there are two cases: nobody has done it yet
or this section needs updating. In both cases, you can
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter either
the [gitter channel](https://gitter.im/skypjack/entt) or the
[discord server](https://discord.gg/5BjPWBd) to ask for help.<br/>
Probably someone already has an answer for you and we can then integrate this
part of the documentation.
# FAQ
## Why is my debug build on Windows so slow?
`EnTT` is an experimental project that I also use to keep me up-to-date with the
latest revision of the language and the standard library. For this reason, it's
likely that some classes you're working with are using standard containers under
the hood.<br/>
Unfortunately, it's known that the standard containers aren't particularly
performing in debugging (the reasons for this go beyond this document) and are
even less so on Windows apparently. Fortunately this can also be mitigated a
lot, achieving good results in many cases.
First of all, there are two things to do in a Windows project:
* Disable the [`/JMC`](https://docs.microsoft.com/cpp/build/reference/jmc)
option (_Just My Code_ debugging), available starting with Visual Studio 2017
version 15.8.
* Set the [`_ITERATOR_DEBUG_LEVEL`](https://docs.microsoft.com/cpp/standard-library/iterator-debug-level)
macro to 0. This will disable checked iterators and iterator debugging.
Moreover, set the `ENTT_DISABLE_ASSERT` variable or redefine the `ENTT_ASSERT`
macro to disable internal debug checks in `EnTT`:
```cpp
#define ENTT_ASSERT(...) ((void)0)
```
These asserts are introduced to help the users but they require to access to the
underlying containers and therefore risk ruining the performance in some cases.
With these changes, debug performance should increase enough in most cases. If
you want something more, you can also switch to an optimization level `O0` or
preferably `O1`.
## How can I represent hierarchies with my components?
This is one of the first questions that anyone makes when starting to work with
the entity-component-system architectural pattern.<br/>
There are several approaches to the problem and the best one depends mainly on
the real problem one is facing. In all cases, how to do it doesn't strictly
depend on the library in use, but the latter certainly allows or not different
techniques depending on how the data are laid out.
I tried to describe some of the approaches that fit well with the model of
`EnTT`. [This](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
first post of a series that tries to _explore_ the problem. More will probably
come in future.<br/>
In addition, `EnTT` also offers the possibility to create stable storage types
and therefore have pointer stability for one, all or some components. This is by
far the most convenient solution when it comes to creating hierarchies and
whatnot. See the documentation for the ECS part of the library and in particular
what concerns the `component_traits` class for further details.
## Custom entity identifiers: yay or nay?
Custom entity identifiers are definitely a good idea in two cases at least:
* If `std::uint32_t` isn't large enough for your purposes, since this is the
underlying type of `entt::entity`.
* If you want to avoid conflicts when using multiple registries.
Identifiers can be defined through enum classes and class types that define an
`entity_type` member of type `std::uint32_t` or `std::uint64_t`.<br/>
In fact, this is a definition equivalent to that of `entt::entity`:
```cpp
enum class entity: std::uint32_t {};
```
There is no limit to the number of identifiers that can be defined.
## Warning C4307: integral constant overflow
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
hashed string under VS (toolset v141) could generate a warning.<br/>
First of all, I want to reassure you: it's expected and harmless. However, it
can be annoying.
To suppress it and if you don't want to suppress all the other warnings as well,
here is a workaround in the form of a macro:
```cpp
#if defined(_MSC_VER)
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
#else
#define HS(str) entt::hashed_string{str}
#endif
```
With an example of use included:
```cpp
constexpr auto identifier = HS("my/resource/identifier");
```
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
## Warning C4003: the min, the max and the macro
On Windows, a header file defines two macros `min` and `max` which may result in
conflicts with their counterparts in the standard library and therefore in
errors during compilation.
It's a pretty big problem but fortunately it's not a problem of `EnTT` and there
is a fairly simple solution to it.<br/>
It consists in defining the `NOMINMAX` macro before including any other header
so as to get rid of the extra definitions:
```cpp
#define NOMINMAX
```
Please refer to [this](https://github.com/skypjack/entt/issues/96) issue for
more details.
## The standard and the non-copyable types
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
component is actually copyable. However, this trait doesn't really check whether
a type is actually copyable. Instead, it just checks that a suitable copy
constructor and copy operator exist.<br/>
This can lead to surprising results due to some idiosyncrasies of the standard.
For example, `std::vector` defines a copy constructor that is conditionally
enabled depending on whether the value type is copyable or not. As a result,
`std::is_copy_constructible_v` returns true for the following specialization:
```cpp
struct type {
std::vector<std::unique_ptr<action>> vec;
};
```
However, the copy constructor is effectively disabled upon specialization.
Therefore, trying to assign an instance of this type to an entity may trigger a
compilation error.<br/>
As a workaround, users can mark the type explicitly as non-copyable. This also
suppresses the implicit generation of the move constructor and operator, which
will therefore have to be defaulted accordingly:
```cpp
struct type {
type(const type &) = delete;
type(type &&) = default;
type & operator=(const type &) = delete;
type & operator=(type &&) = default;
std::vector<std::unique_ptr<action>> vec;
};
```
Note that aggregate initialization is also disabled as a consequence.<br/>
Fortunately, this type of trick is quite rare. The bad news is that there is no
way to deal with it at the library level, this being due to the design of the
language. On the other hand, the fact that the language itself also offers a way
to mitigate the problem makes it manageable.
## Which functions trigger which signals
The `registry` class offers three signals that are emitted following specific
operations. Maybe not everyone knows what these operations are, though.<br/>
If this isn't clear, below you can find a _vademecum_ for this purpose:
* `on_created` is invoked when a component is first added (neither modified nor
replaced) to an entity.
* `on_update` is called whenever an existing component is modified or replaced.
* `on_destroyed` is called when a component is explicitly or implicitly removed
from an entity.
Among the most controversial functions can be found `emplace_or_replace` and
`destroy`. However, following the above rules, it's quite simple to know what
will happen.<br/>
In the first case, `on_created` is invoked if the entity has not the component,
otherwise the latter is replaced and therefore `on_update` is triggered. As for
the second case, components are removed from their entities and thus freed when
they are recycled. It means that `on_destroyed` is triggered for every component
owned by the entity that is destroyed.

299
docs/md/graph.md Normal file
View File

@ -0,0 +1,299 @@
# Crash Course: graph
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Data structures](#data-structures)
* [Adjacency matrix](#adjacency-matrix)
* [Graphviz dot language](#graphviz-dot-language)
* [Flow builder](#flow-builder)
* [Tasks and resources](#tasks-and-resources)
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
* [Sync points](#sync-points)
* [Execution graph](#execution-graph)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore,
anyone looking for this in the _graph_ submodule will be disappointed.<br/>
Quite the opposite is true. This submodule is minimal and contains only the data
structures and algorithms strictly necessary for the development of some tools
such as the _flow builder_.
# Data structures
As anticipated in the introduction, the aim isn't to offer all possible data
structures suitable for representing and working with graphs. Many will likely
be added or refined over time, however I want to discourage anyone expecting
tight scheduling on the subject.<br/>
The data structures presented in this section are mainly useful for the
development and support of some tools which are also part of the same submodule.
## Adjacency matrix
The adjacency matrix is designed to represent either a directed or an undirected
graph:
```cpp
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
```
The `directed_tag` type _creates_ the graph as directed. There is also an
`undirected_tag` counterpart which creates it as undirected.<br/>
The interface deviates slightly from the typical double indexing of C and offers
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
and modification of an element will take place via the `contains`, `insert` and
`erase` functions rather than a double call to an `operator[]`:
```cpp
if(adjacency_matrix.contains(0u, 1u)) {
adjacency_matrix.erase(0u, 1u);
} else {
adjacency_matrix.insert(0u, 1u);
}
```
Both `insert` and` erase` are idempotent functions which have no effect if the
element already exists or has already been deleted.<br/>
The first one returns an `std::pair` containing the iterator to the element and
a boolean value indicating whether the element has been inserted or was already
present. The second one instead returns the number of deleted elements (0 or 1).
An adjacency matrix must be initialized with the number of elements (vertices)
when constructing it but can also be resized later using the `resize` function:
```cpp
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
```
To visit all vertices, the class offers a function named `vertices` that returns
an iterable object suitable for the purpose:
```cpp
for(auto &&vertex: adjacency_matrix.vertices()) {
// ...
}
```
Note that the same result can be obtained with the following snippet, since the
vertices are unsigned integral values:
```cpp
for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
// ...
}
```
As for visiting the edges, a few functions are available.<br/>
When the purpose is to visit all the edges of a given adjacency matrix, the
`edges` function returns an iterable object that can be used to get them as
pairs of vertices:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
// ...
}
```
On the other hand, if the goal is to visit all the in- or out-edges of a given
vertex, the `in_edges` and `out_edges` functions are meant for that:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
// ...
}
```
As might be expected, these functions expect the vertex to visit (that is, to
return the in- or out-edges for) as an argument.<br/>
Finally, the adjacency matrix is an allocator-aware container and offers most of
the functionality one would expect from this type of containers, such as `clear`
or 'get_allocator` and so on.
## Graphviz dot language
As it's one of the most popular formats, the library offers minimal support for
converting a graph to a Graphviz dot snippet.<br/>
The simplest way is to pass both an output stream and a graph to the `dot`
function:
```cpp
std::ostringstream output{};
entt::dot(output, adjacency_matrix);
```
However, there is also the option of providing a callback to which the vertices
are passed and which can be used to add (`dot`) properties to the output from
time to time:
```cpp
std::ostringstream output{};
entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) {
out << "label=\"v\"" << vertex << ",shape=\"box\"";
});
```
This second mode is particularly convenient when the user wants to associate
data managed externally to the graph being converted.
# Flow builder
A flow builder is used to create execution graphs from tasks and resources.<br/>
The implementation is as generic as possible and doesn't bind to any other part
of the library.
This class is designed as a sort of _state machine_ to which a specific task is
attached for which the resources accessed in read-only or read-write mode are
specified.<br/>
Most of the functions in the API also return the flow builder itself, according
to what is the common sense API when it comes to builder classes.
Once all tasks have been registered and resources assigned to them, an execution
graph in the form of an adjacency matrix is returned to the user.<br/>
This graph contains all the tasks assigned to the flow builder in the form of
_vertices_. The _vertex_ itself can be used as an index to get the identifier
passed during registration.
## Tasks and resources
Although these terms are used extensively in the documentation, the flow builder
has no real concept of tasks and resources.<br/>
This class works mainly with _identifiers_, that is, values of type `id_type`.
That is, both tasks and resources are identified by integral values.<br/>
This allows not to couple the class itself to the rest of the library or to any
particular data structure. On the other hand, it requires the user to keep track
of the association between identifiers and operations or actual data.
Once a flow builder has been created (which requires no constructor arguments),
the first thing to do is to bind a task. This will indicate to the builder who
intends to consume the resources that will be specified immediately after:
```cpp
entt::flow builder{};
builder.bind("task_1"_hs);
```
Note that the example uses the `EnTT` hashed string to generate an identifier
for the task.<br/>
Indeed, the use of `id_type` as an identifier type is not by accident. In fact,
it matches well with the internal hashed string class. Moreover, it's also the
same type returned by the hash function of the internal RTTI system, in case the
user wants to rely on that.<br/>
However, being an integral value, it leaves the user full freedom to rely on his
own tools if he deems it necessary.
Once a task has been associated with the flow builder, it can be assigned
read-only or read-write resources, as appropriate:
```cpp
builder
.bind("task_1"_hs)
.ro("resource_1"_hs)
.ro("resource_2"_hs)
.bind("task_2"_hs)
.rw("resource_2"_hs)
```
As mentioned, many functions return the builder itself and it's therefore easy
to concatenate the different calls.<br/>
Also in the case of resources, these are identified by numeric values of type
`id_type`. As above, the choice is not entirely random. This goes well with the
tools offered by the library while leaving room for maximum flexibility.
Finally, both the `ro` and` rw` functions also offer an overload that accepts a
pair of iterators, so that one can pass a range of resources in one go.
## Fake resources and order of execution
The flow builder doesn't offer the ability to specify when a task should execute
before or after another task.<br/>
In fact, the order of _registration_ on the resources also determines the order
in which the tasks are processed during the generation of the execution graph.
However, there is a way to force the execution order of two processes.<br/>
Briefly, since accessing a resource in opposite modes requires sequential rather
than parallel scheduling, it's possible to make use of fake resources to force
the order execution:
```cpp
builder
.bind("task_1"_hs)
.ro("resource_1"_hs)
.rw("fake"_hs)
.bind("task_2"_hs)
.ro("resource_2"_hs)
.ro("fake"_hs)
.bind("task_3"_hs)
.ro("resource_2"_hs)
.ro("fake"_hs)
```
This snippet forces the execution of `task_2` and `task_3` **after** `task_1`.
This is due to the fact that the latter sets a read-write requirement on a fake
resource that the other tasks also want to access in read-only mode.<br/>
Similarly, it's possible to force a task to run after a certain group:
```cpp
builder
.bind("task_1"_hs)
.ro("resource_1"_hs)
.ro("fake"_hs)
.bind("task_2"_hs)
.ro("resource_1"_hs)
.ro("fake"_hs)
.bind("task_3"_hs)
.ro("resource_2"_hs)
.rw("fake"_hs)
```
In this case, since there are a number of processes that want to read a specific
resource, they will do so in parallel by forcing `task_3` to run after all the
others tasks.
## Sync points
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
Whether it accesses new resources or is simply a watershed, the procedure for
assigning this role to a vertex is always the same: first it's tied to the flow
builder, then the `sync` function is invoked:
```cpp
builder.bind("sync_point"_hs).sync();
```
The choice to assign an _identity_ to this type of nodes lies in the fact that,
more often than not, they also perform operations on resources.<br/>
If this isn't the case, it will still be possible to create no-op vertices to
which empty tasks are assigned.
## Execution graph
Once both the resources and their consumers have been properly registered, the
purpose of this tool is to generate an execution graph that takes into account
all specified constraints to return the best scheduling for the vertices:
```cpp
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
```
The search for the main vertices, that is those without in-edges, is usually the
first thing required:
```cpp
for(auto &&vertex: graph) {
if(auto in_edges = graph.in_edges(vertex); in_edges.begin() == in_edges.end()) {
// ...
}
}
```
Starting from them, using the other functions appropriately (such as `out_edges`
to retrieve the children of a given task or `edges` to access their identifiers)
it will be possible to instantiate an execution graph.

99
docs/md/lib.md Normal file
View File

@ -0,0 +1,99 @@
# Push EnTT across boundaries
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Working across boundaries](#working-across-boundaries)
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Working across boundaries
`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 was mainly due to a custom utility used to assign unique, sequential
identifiers with different types.<br/>
Fortunately, nowadays using `EnTT` across boundaries is much easier.
## Smooth until proven otherwise
Many classes in `EnTT` make extensive use of type erasure for their purposes.
This isn't a problem on itself (in fact, it's the basis of an API so convenient
to use). However, a way is needed to recognize the objects whose type has been
erased on the other side of a boundary.<br/>
The `type_hash` class template is how identifiers are generated and thus made
available to the rest of the library. In general, this class doesn't arouse much
interest. The only exception is when a conflict between identifiers occurs
(definitely uncommon though) or when the default solution proposed by `EnTT`
isn't suitable for the user's purposes.<br/>
The section dedicated to `type_info` contains all the details to get around the
issue in a concise and elegant way. Please refer to the specific documentation.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` can be used where there is a need to import or export symbols,
so as to make everything work nicely across boundaries.<br/>
On the other hand, everything should run smoothly when working with plugins or
shared libraries that don't export any symbols.
For anyone who needs more details, the test suite contains multiple examples
covering the most common cases (see the `lib` directory for all details).<br/>
It goes without saying that it's impossible to cover **all** possible cases.
However, what is offered should hopefully serve as a basis for all of them.
## Meta context
The runtime reflection system deserves a special mention when it comes to using
it across boundaries.<br/>
Since it's linked already to a static context to which the elements are attached
and different contexts don't relate to each other, they must be _shared_ to
allow the use of meta types across boundaries.
Fortunately, sharing a context is also trivial to do. First of all, the local
one is acquired in the main space:
```cpp
auto handle = entt::locator<entt::meta_ctx>::handle();
```
Then, it's passed to the receiving space that sets it as its default context,
thus discarding or storing aside the local one:
```cpp
entt::locator<entt::meta_ctx>::reset(handle);
```
From now on, both spaces refer to the same context and on it are attached all
new meta types, no matter where they are created.<br/>
Note that resetting the main context doesn't also propagate changes across
boundaries. In other words, resetting a context results in the decoupling of the
two sides and therefore a divergence in the contents.
## Memory Management
There is another subtle problem due to memory management that can lead to
headaches.<br/>
It can occur where there are pools of objects (such as components or events)
dynamically created on demand. This is usually not a problem when working with
linked libraries that rely on the same dynamic runtime. However, it can occur in
the case of plugins or statically linked runtimes.
As an example, imagine creating an instance of `registry` in the main executable
and sharing it with a plugin. If the latter starts working with a component that
is unknown to the former, a dedicated pool is created within the registry on
first use.<br/>
As one can guess, this pool is instantiated on a different side of the boundary
from the `registry`. Therefore, the instance is now managing memory from
different spaces and this can quickly lead to crashes if not properly addressed.
To overcome the risk, it's recommended to use well-defined interfaces that make
fundamental types pass through the boundaries, isolating the instances of the
`EnTT` classes from time to time and as appropriate.<br/>
Refer to the test suite for some examples, read the documentation available
online about this type of issues or consult someone who has already had such
experiences to avoid problems.

266
docs/md/links.md Normal file
View File

@ -0,0 +1,266 @@
# EnTT in Action
`EnTT` is widely used in private and commercial applications. I cannot even
mention most of them because of some signatures I put on some documents time
ago. Fortunately, there are also people who took the time to implement open
source projects based on `EnTT` and didn't hold back when it came to documenting
them.
Below an incomplete list of games, applications and articles that can be used as
a reference. Where I put the word _apparently_ means that the use of `EnTT` is
documented but the authors didn't make explicit announcements or contacted me
directly.
I hope this list can grow much more in the future:
* Games:
* [Minecraft](https://minecraft.net/en-us/attribution/) by
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
open source attributions page for more details.
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
lets users bring Minecraft into the real world.
* [Ember Sword](https://embersword.com/): a modern Free-to-Play MMORPG with a
player-driven economy, a classless combat system, and scarce, tradable
cosmetic collectibles.
* Apparently [Diablo II: Resurrected](https://diablo2.blizzard.com/) by
[Blizzard](https://www.blizzard.com/): monsters, heroes, items, spells, all
resurrected. Thanks unknown insider.
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard) by
[Sledgehammer Games](https://www.sledgehammergames.com/): I can neither
confirm nor deny but there is a license I know in the credits.
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
[Wizards of the Coast](https://company.wizards.com): your party, their
funeral.
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
to play online.
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
simulation [game](https://antkeeper.com/).
* [Openblack](https://github.com/openblack/openblack): open source
reimplementation of the game _Black & White_ (2001).
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
of [a retro-style MUD](https://rair.land/) for the new age.
* [Face Smash](https://play.google.com/store/apps/details?id=com.gamee.facesmash):
a game to play with your face.
* [EnTT Pacman](https://github.com/Kerndog73/EnTT-Pacman): an example of how
to make Pacman with `EnTT`.
* [Wacman](https://github.com/carlfindahl/wacman): a pacman clone with OpenGL.
* [Classic Tower Defence](https://github.com/kerndog73/Classic-Tower-Defence):
a tiny little tower defence game featuring a homemade font.
[Check it out](https://indi-kernick.itch.io/classic-tower-defence).
* [The Machine](https://github.com/Kerndog73/The-Machine): a box pushing
puzzler with logic gates and other cool stuff.
[Check it out](https://indi-kernick.itch.io/the-machine-web-version).
* [EnTTPong](https://github.com/DomRe/EnttPong): a basic game made to showcase
different parts of `EnTT` and C++17.
* [Randballs](https://github.com/gale93/randballs): simple `SFML` and `EnTT`
playground.
* [EnTT Tower Defense](https://github.com/Daivuk/tddod): a data oriented tower
defense example.
* [EnTT Breakout](https://github.com/vblanco20-1/entt-breakout): simple
example of a breakout game, using `SDL` and `EnTT`.
* [Arcade puzzle game with EnTT](https://github.com/MasonRG/ArcadePuzzleGame):
arcade puzzle game made in C++ using the `SDL2` and `EnTT` libraries.
* [Snake with EnTT](https://github.com/MasonRG/SnakeGame): simple snake game
made in C++ with the `SDL2` and `EnTT` libraries.
* [Mirrors lasers and robots](https://github.com/guillaume-haerinck/imac-tower-defense):
a small tower defense game based on mirror orientation.
* [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead/): 2D, Zombie,
RPG game made from scratch in C++.
* [Robotligan](https://github.com/Trisslotten/robotligan): multiplayer
football game.
* [DungeonSlayer](https://github.com/alohaeee/DungeonSlayer): 2D game made
from scratch in C++.
* [3DGame](https://github.com/kwarkGorny/3DGame): 2.5D top-down space shooter.
* [Pulcher](https://github.com/AODQ/pulcher): 2D cross-platform game inspired
by Quake.
* [Destroid](https://github.com/tyrannicaltoucan/destroid): _one-bazillionth_
arcade game about shooting dirty rocks in space, inspired by Asteroids.
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D exploration
based indie game.
* [Spelunky<EFBFBD> Classic remake](https://github.com/dbeef/spelunky-psp): a truly
multiplatform experience with a rewrite from scratch.
* [CubbyTower](https://github.com/utilForever/CubbyTower): a simple tower
defense game using C++ with Entity Component System (ECS).
* [Runeterra](https://github.com/utilForever/Runeterra): Legends of Runeterra
simulator using C++ with some reinforcement learning.
* [Black Sun](https://store.steampowered.com/app/1670930/Black_Sun/): fly your
space ship through a large 2D open world.
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokemon Battle
simulator using C++ with some reinforcement learning.
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
town, before it's too late.
* [City Builder Game](https://github.com/PhiGei2000/CityBuilderGame): a simple
city-building game using C++ and OpenGL.
* [BattleSub](https://github.com/bfeldpw/battlesub): two player 2D submarine
game with some fluid dynamics.
* [Crimson Rush](https://github.com/WilKam01/LuaCGame): a dungeon-crawler and
rougelike inspired game about exploring and surviving as long as possible.
* [Space Fight](https://github.com/cholushkin/SpaceFight): one screen
multi-player arcade shooter game prototype.
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
application as a starting point using `EnTT` and `SDL2`.
* Engines and the like:
* [Aether Engine](https://hadean.com/spatial-simulation/)
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
[Hadean](https://hadean.com/): a library designed for spatially partitioning
agent-based simulations.
* [Fling Engine](https://github.com/flingengine/FlingEngine): a Vulkan game
engine with a focus on data oriented design.
* [NovusCore](https://github.com/novuscore/NovusCore): a modern take on World
of Warcraft emulation.
* [Chrysalis](https://github.com/ivanhawkes/Chrysalis): action RPG SDK for
CRYENGINE games.
* [LM-Engine](https://github.com/Lawrencemm/LM-Engine): the Vim of game
engines.
* [Edyn](https://github.com/xissburg/edyn): a real-time physics engine
organized as an ECS.
* [MushMachine](https://github.com/MadeOfJelly/MushMachine): engine...
vrooooommm.
* [Antara Gaming SDK](https://github.com/KomodoPlatform/antara-gaming-sdk):
the Komodo Gaming Software Development Kit.
* [XVP](https://ravingbots.com/xvp-expansive-vehicle-physics-for-unreal-engine/):
[_eXpansive Vehicle Physics_](https://github.com/raving-bots/xvp/wiki/Plugin-integration-guide)
plugin for Unreal Engine.
* [Apparently](https://teamwisp.github.io/credits/)
[Wisp](https://teamwisp.github.io/product/) by
[Team Wisp](https://teamwisp.github.io/): an advanced real-time ray tracing
renderer built for the demands of video game artists.
* [shiva](https://github.com/Milerius/shiva): modern C++ engine with
modularity.
* [ImGui/EnTT editor](https://github.com/Green-Sky/imgui_entt_entity_editor):
a drop-in, single-file entity editor for `EnTT` that uses `ImGui` as
graphical backend (with
[demo code](https://github.com/Green-Sky/imgui_entt_entity_editor_demo)).
* [SgOgl](https://github.com/stwe/SgOgl): a game engine library for OpenGL
developed for educational purposes.
* [Lumos](https://github.com/jmorton06/Lumos): game engine written in C++
using OpenGL and Vulkan.
* [Silvanus](https://github.com/hobbyistmaker/silvanus): Silvanus Fusion 360
Box Generator.
* [Lina Engine](https://github.com/inanevin/LinaEngine): an open-source,
modular, tiny and fast C++ game engine, aimed to develop 3D desktop games.
* [Spike](https://github.com/FahimFuad/Spike): a powerful game engine which
can run on a toaster.
* [Helena Framework](https://github.com/NIKEA-SOFT/HelenaFramework): a modern
framework in C++17 for backend development.
* [Unity/EnTT](https://github.com/TongTungGiang/unity-entt): tech demo of a
native simulation layer using `EnTT` and `Unity` as a rendering engine.
* [OverEngine](https://github.com/OverShifted/OverEngine): an over-engineered
game engine.
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
3D game engine with a high emphasis on rendering.
* [Kawaii](https://github.com/Mathieu-Lala/Kawaii_Engine): a modern data
oriented game engine.
* [Becketron](https://github.com/Doctor-Foxling/Becketron): a game engine
written mostly in C++.
* [Spatial Engine](https://github.com/luizgabriel/Spatial.Engine): a
cross-platform engine created on top of google's filament rendering engine.
* [Kaguya](https://github.com/KaiH0717/Kaguya): D3D12 Rendering Engine.
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): open implementation
of the Alan Wake Engine.
* [Nazara Engine](https://github.com/DigitalPulseSoftware/NazaraEngine): fast,
cross-platform, object-oriented API to help in daily developer life.
* [Billy Engine](https://github.com/billy4479/BillyEngine): some kind of a 2D
engine based on `SDL2` and `EnTT`.
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
2D & 3D game engine that focuses on being fast and powerful.
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
know **more** on this project.
* [Game Engine series](https://www.youtube.com/c/TheChernoProject/videos) by
[The Cherno](https://github.com/TheCherno) (not only about `EnTT` but also
on the use of an ECS in general):
- [Intro to EnTT](https://www.youtube.com/watch?v=D4hz0wEB978).
- [Entities and Components](https://www.youtube.com/watch?v=-B1iu4QJTUc).
- [The ENTITY Class](https://www.youtube.com/watch?v=GfSzeAcsBb0).
- [Camera Systems](https://www.youtube.com/watch?v=ubZn7BlrnTU).
- [Scene Camera](https://www.youtube.com/watch?v=UKVFRRufKzo).
- [Native Scripting](https://www.youtube.com/watch?v=iIUhg88MK5M).
- [Native Scripting (now with virtual functions!)](https://www.youtube.com/watch?v=1cHEcrIn8IQ).
- [Scene Hierarchy Panel](https://www.youtube.com/watch?v=wziDnE8guvI).
- [Properties Panel](https://www.youtube.com/watch?v=NBpB0qscF3E).
- [Camera Component UI](https://www.youtube.com/watch?v=RIMt_6agUiU).
- [Drawing Component UI](https://www.youtube.com/watch?v=u3yq8s3KuSE).
- [Transform Component UI](https://www.youtube.com/watch?v=8JqcXYbzPJc).
- [Adding/Removing Entities and Components UI](https://www.youtube.com/watch?v=PsyGmsIgp9M).
- [Saving and Loading Scenes](https://www.youtube.com/watch?v=IEiOP7Y-Mbc).
- ... And so on.
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
_Game Engine Series_ by The Cherno for more videos.
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
huge space battle built entirely from scratch.
* [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
battle built on `UE4`.
* [Experimenting with ECS in UE4](http://victor.madtriangles.com/code%20experiment/2018/03/25/post-ue4-ecs-battle.html):
interesting article about `UE4` and `EnTT`.
* [Implementing ECS architecture in UE4](https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1449913-implementing-ecs-architecture-in-ue4-giant-space-battle):
giant space battle.
* [Conan Adventures (SFML and EnTT in C++)](https://leinnan.github.io/blog/conan-adventuressfml-and-entt-in-c.html):
create projects in modern C++ using `SFML`, `EnTT`, `Conan` and `CMake`.
* [Adding EnTT ECS to Chrysalis](https://www.tauradius.com/post/adding-an-ecs-to-chrysalis/):
a blog entry (and its
[follow-up](https://www.tauradius.com/post/chrysalis-update-2020-08-02/))
about the integration of `EnTT` into `Chrysalis`, an action RPG SDK for
CRYENGINE games.
* [Creating Minecraft in One Week with C++ and Vulkan](https://vazgriz.com/189/creating-minecraft-in-one-week-with-c-and-vulkan/):
a crack at recreating Minecraft in one week using a custom C++ engine and
Vulkan ([code included](https://github.com/vazgriz/VoxelGame)).
* [Ability Creator](https://www.erichildebrand.net/blog/ability-creator-project-retrospect):
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
* [EnTT Entity Component System Gaming Library](https://gamefromscratch.com/entt-entity-component-system-gaming-library/):
`EnTT` on GameFromScratch.com.
* [Custom C++ server for UE5](https://youtu.be/fbXZVNCOvjM) optimized for
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
player bots and full external ECS: a series definitely worth looking at.
* Any Other Business:
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
cross platform C++ rendering engine. The SDKs are utilized by a lot of
enterprise custom apps, as well as by Esri for its own public applications
such as
[Explorer](https://play.google.com/store/apps/details?id=com.esri.explorer),
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
and
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
`EnTT` to drive their simulation, that is, the communication between robot
controller emulator and renderer.
* [Ragdoll](https://ragdolldynamics.com/): real-time physics for Autodesk Maya
2020.
* [Project Lagrange](https://github.com/adobe/lagrange): a robust geometry
processing library by [Adobe](https://github.com/adobe).
* [AtomicDEX](https://github.com/KomodoPlatform/atomicDEX-Desktop): a secure
wallet and non-custodial decentralized exchange rolled into one application.
* [Apparently](https://www.linkedin.com/in/skypjack/)
[NIO](https://www.nio.io/): there was a collaboration to make some changes
to `EnTT`, at the time used for internal projects.
* [Apparently](https://www.linkedin.com/jobs/view/architekt-c%2B%2B-at-tieto-1219512333/)
[Tieto](https://www.tieto.com/): they published a job post where `EnTT` was
listed on their software stack.
* [Sequentity](https://github.com/alanjfs/sequentity): A MIDI-like
sequencer/tracker for C++ and `ImGui` (with `Magnum` and `EnTT`).
* [EnTT meets Sol2](https://github.com/skaarj1989/entt-meets-sol2): freely
available examples of how to combine `EnTT` and `Sol2`.
* [Godot meets EnTT](https://github.com/portaloffreedom/godot_entt_example/):
a simple example on how to use `EnTT` within
[`Godot`](https://godotengine.org/).
* [Godot and GameNetworkingSockets meet EnTT](https://github.com/portaloffreedom/godot_entt_net_example):
a simple example on how to use `EnTT` and
[`GameNetworkingSockets`](https://github.com/ValveSoftware/GameNetworkingSockets)
within [`Godot`](https://godotengine.org/).
* [MatchOneEntt](https://github.com/mhaemmerle/MatchOneEntt): port of
[Match One](https://github.com/sschmid/Match-One) for `Entitas-CSharp`.
* GitHub contains also
[many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code)
of use of `EnTT` from which to take inspiration if interested.
If you know of other resources out there that are about `EnTT`, feel free to
open an issue or a PR and I'll be glad to add them to this page.

88
docs/md/locator.md Normal file
View File

@ -0,0 +1,88 @@
# Crash Course: service locator
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Service locator](#service-locator)
* [Opaque handles](#opaque-handles)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Usually, service locators are tightly bound to the services they expose and it's
hard to define a general purpose solution.<br/>
This tiny class tries to fill the gap and gets rid of the burden of defining a
different specific locator for each application.
# Service locator
The service locator API tries to mimic that of `std::optional` and adds some
extra functionalities on top of it such as allocator support.<br/>
There are a couple of functions to set up a service, namely `emplace` and
`allocate_emplace`:
```cpp
entt::locator<interface>::emplace<service>(argument);
entt::locator<interface>::allocate_emplace<service>(allocator, argument);
```
The difference is that the latter expects an allocator as the first argument and
uses it to allocate the service itself.<br/>
Once a service is set up, it's retrieved using the `value` function:
```cpp
interface &service = entt::locator<interface>::value();
```
Since the service may not be set (and therefore this function may result in an
undefined behavior), the `has_value` and `value_or` functions are also available
to test a service locator and to get a fallback service in case there is none:
```cpp
if(entt::locator<interface>::has_value()) {
// ...
}
interface &service = entt::locator<interface>::value_or<fallback_impl>(argument);
```
All arguments are used only if necessary, that is, if a service doesn't already
exist and therefore the fallback service is constructed and returned. In all
other cases, they are discarded.<br/>
Finally, to reset a service, use the `reset` function.
## Opaque handles
Sometimes it's useful to _transfer_ a copy of a service to another locator. For
example, when working across boundaries it's common to _share_ a service with a
dynamically loaded module.<br/>
Options aren't much in this case. Among these is the possibility of _exporting_
services and assigning them to a different locator.
This is what the `handle` and `reset` functions are meant for.<br/>
The former returns an opaque object useful for _exporting_ (or rather, obtaining
a reference to) a service. The latter also accepts an optional argument to a
handle which then allows users to reset a service by initializing it with an
opaque handle:
```cpp
auto handle = entt::locator<interface>::handle();
entt::locator<interface>::reset(handle);
```
It's worth noting that it's possible to get handles for uninitialized services
and use them with other locators. Of course, all a user will get is to have an
uninitialized service elsewhere as well.
Note that exporting a service allows users to _share_ the object currently set
in a locator. Replacing it won't replace the element even where a service has
been configured with a handle to the previous item.<br/>
In other words, if an audio service is replaced with a null object to silence an
application and the original service was shared, this operation won't propagate
to the other locators. Therefore, a module that share the ownership of the
original audio service is still able to emit sounds.

1023
docs/md/meta.md Normal file

File diff suppressed because it is too large Load Diff

359
docs/md/poly.md Normal file
View File

@ -0,0 +1,359 @@
# Crash Course: poly
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Other libraries](#other-libraries)
* [Concept and implementation](#concept-and-implementation)
* [Deduced interface](#deduced-interface)
* [Defined interface](#defined-interface)
* [Fulfill a concept](#fulfill-a-concept)
* [Inheritance](#inheritance)
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome
to obtain.<br/>
This module aims to make it simple and easy to use.
The library allows to define _concepts_ as interfaces to fulfill with concrete
classes without having to inherit from a common base.<br/>
This is, among others, one of the advantages of static polymorphism in general
and of a generic wrapper like that offered by the `poly` class template in
particular.<br/>
What users get is an object that can be passed around as such and not through a
reference or a pointer, as happens when it comes to working with dynamic
polymorphism.
Since the `poly` class template makes use of `entt::any` internally, it also
supports most of its feature. Among the most important, the possibility to
create aliases to existing and thus unmanaged objects. This allows users to
exploit the static polymorphism while maintaining ownership of objects.<br/>
Likewise, the `poly` class template also benefits from the small buffer
optimization offered by the `entt::any` class and therefore minimizes the number
of allocations, avoiding them altogether where possible.
## Other libraries
There are some very interesting libraries regarding static polymorphism.<br/>
Among all, the two that I prefer are:
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
a class template that makes it easy to define a type-erasing polymorphic
object wrapper.
The former is admittedly an experimental library, with many interesting ideas.
I've some doubts about the usefulness of some feature in real world projects,
but perhaps my lack of experience comes into play here. In my opinion, its only
flaw is the API which I find slightly more cumbersome than other solutions.<br/>
The latter was undoubtedly a source of inspiration for this module, although I
opted for different choices in the implementation of both the final API and some
feature.
Either way, the authors are gurus of the C++ community, people I only have to
learn from.
# Concept and implementation
The first thing to do to create a _type-erasing polymorphic object wrapper_ (to
use the terminology introduced by Eric Niebler) is to define a _concept_ that
types will have to adhere to.<br/>
For this purpose, the library offers a single class that supports both deduced
and fully defined interfaces. Although having interfaces deduced automatically
is convenient and allows users to write less code in most cases, this has some
limitations and it's therefore useful to be able to get around the deduction by
providing a custom definition for the static virtual table.
Once the interface is defined, it will be sufficient to provide a generic
implementation to fulfill the concept.<br/>
Also in this case, the library allows customizations based on types or families
of types, so as to be able to go beyond the generic case where necessary.
## Deduced interface
This is how a concept with a deduced interface is introduced:
```cpp
struct Drawable: entt::type_list<> {
template<typename Base>
struct type: Base {
void draw() { this->template invoke<0>(*this); }
};
// ...
};
```
It's recognizable by the fact that it inherits from an empty type list.<br/>
Functions can also be const, accept any number of parameters and return a type
other than `void`:
```cpp
struct Drawable: entt::type_list<> {
template<typename Base>
struct type: Base {
bool draw(int pt) const { return this->template invoke<0>(*this, pt); }
};
// ...
};
```
In this case, all parameters must be passed to `invoke` after the reference to
`this` and the return value is whatever the internal call returns.<br/>
As for `invoke`, this is a name that is injected into the _concept_ through
`Base`, from which one must necessarily inherit. Since it's also a dependent
name, the `this-> template` form is unfortunately necessary due to the rules of
the language. However, there exists also an alternative that goes through an
external call:
```cpp
struct Drawable: entt::type_list<> {
template<typename Base>
struct type: Base {
void draw() const { entt::poly_call<0>(*this); }
};
// ...
};
```
Once the _concept_ is defined, users must provide a generic implementation of it
in order to tell the system how any type can satisfy its requirements. This is
done via an alias template within the concept itself.<br/>
The index passed as a template parameter to either `invoke` or `poly_call`
refers to how this alias is defined.
## Defined interface
A fully defined concept is no different to one for which the interface is
deduced, with the only difference that the list of types is not empty this time:
```cpp
struct Drawable: entt::type_list<void()> {
template<typename Base>
struct type: Base {
void draw() { entt::poly_call<0>(*this); }
};
// ...
};
```
Again, parameters and return values other than `void` are allowed. Also, the
function type must be const when the method to bind to it is const:
```cpp
struct Drawable: entt::type_list<bool(int) const> {
template<typename Base>
struct type: Base {
bool draw(int pt) const { return entt::poly_call<0>(*this, pt); }
};
// ...
};
```
Why should a user fully define a concept if the function types are the same as
the deduced ones?<br>
Because, in fact, this is exactly the limitation that can be worked around by
manually defining the static virtual table.
When things are deduced, there is an implicit constraint.<br/>
If the concept exposes a member function called `draw` with function type
`void()`, a concept can be satisfied:
* Either by a class that exposes a member function with the same name and the
same signature.
* Or through a lambda that makes use of existing member functions from the
interface itself.
In other words, it's not possible to make use of functions not belonging to the
interface, even if they are present in the types that fulfill the concept.<br/>
Similarly, it's not possible to deduce a function in the static virtual table
with a function type different from that of the associated member function in
the interface itself.
Explicitly defining a static virtual table suppresses the deduction step and
allows maximum flexibility when providing the implementation for a concept.
## Fulfill a concept
The `impl` alias template of a concept is used to define how it's fulfilled:
```cpp
struct Drawable: entt::type_list<> {
// ...
template<typename Type>
using impl = entt::value_list<&Type::draw>;
};
```
In this case, it's stated that the `draw` method of a generic type will be
enough to satisfy the requirements of the `Drawable` concept.<br/>
Both member functions and free functions are supported to fulfill concepts:
```cpp
template<typename Type>
void print(Type &self) { self.print(); }
struct Drawable: entt::type_list<void()> {
// ...
template<typename Type>
using impl = entt::value_list<&print<Type>>;
};
```
Likewise, as long as the parameter types and return type support conversions to
and from those of the function type referenced in the static virtual table, the
actual implementation may differ in its function type since it's erased
internally.<br/>
Moreover, the `self` parameter isn't strictly required by the system and can be
left out for free functions if not required.
Refer to the inline documentation for more details.
# Inheritance
_Concept inheritance_ is straightforward due to how poly looks like in `EnTT`.
Therefore, it's quite easy to build hierarchies of concepts if necessary.<br/>
The only constraint is that all concepts in a hierarchy must belong to the same
_family_, that is, they must be either all deduced or all defined.
For a deduced concept, inheritance is achieved in a few steps:
```cpp
struct DrawableAndErasable: entt::type_list<> {
template<typename Base>
struct type: typename Drawable::template type<Base> {
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
void erase() { entt::poly_call<base + 0>(*this); }
};
template<typename Type>
using impl = entt::value_list_cat_t<
typename Drawable::impl<Type>,
entt::value_list<&Type::erase>
>;
};
```
The static virtual table is empty and must remain so.<br/>
On the other hand, `type` no longer inherits from `Base` and instead forwards
its template parameter to the type exposed by the _base class_. Internally, the
size of the static virtual table of the base class is used as an offset for the
local indexes.<br/>
Finally, by means of the `value_list_cat_t` utility, the implementation consists
in appending the new functions to the previous list.
As for a defined concept instead, also the list of types must be extended, in a
similar way to what is shown for the implementation of the above concept.<br/>
To do this, it's useful to declare a function that allows to convert a _concept_
into its underlying `type_list` object:
```cpp
template<typename... Type>
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
```
The definition isn't strictly required, since the function will only be used
through a `decltype` as it follows:
```cpp
struct DrawableAndErasable: entt::type_list_cat_t<
decltype(as_type_list(std::declval<Drawable>())),
entt::type_list<void()>
> {
// ...
};
```
Similar to above, `type_list_cat_t` is used to concatenate the underlying static
virtual table with the new function types.<br/>
Everything else is the same as already shown instead.
# Static polymorphism in the wild
Once the _concept_ and implementation have been introduced, it will be possible
to use the `poly` class template to contain instances that meet the
requirements:
```cpp
using drawable = entt::poly<Drawable>;
struct circle {
void draw() { /* ... */ }
};
struct square {
void draw() { /* ... */ }
};
// ...
drawable instance{circle{}};
instance->draw();
instance = square{};
instance->draw();
```
The `poly` class template offers a wide range of constructors, from the default
one (which will return an uninitialized `poly` object) to the copy and move
constructors, as well as the ability to create objects in-place.<br/>
Among others, there is also a constructor that allows users to wrap unmanaged
objects in a `poly` instance (either const or non-const ones):
```cpp
circle shape;
drawable instance{std::in_place_type<circle &>, shape};
```
Similarly, it's possible to create non-owning copies of `poly` from an existing
object:
```cpp
drawable other = instance.as_ref();
```
In both cases, although the interface of the `poly` object doesn't change, it
won't construct any element or take care of destroying the referenced objects.
Note also how the underlying concept is accessed via a call to `operator->` and
not directly as `instance.draw()`.<br/>
This allows users to decouple the API of the wrapper from that of the concept.
Therefore, where `instance.data()` will invoke the `data` member function of the
poly object, `instance->data()` will map directly to the functionality exposed
by the underlying concept.
# Storage size and alignment requirement
Under the hood, the `poly` class template makes use of `entt::any`. Therefore,
it can take advantage of the possibility of defining at compile-time the size of
the storage suitable for the small buffer optimization as well as the alignment
requirements:
```cpp
entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
```
The default size is `sizeof(double[2])`, which seems like a good compromise
between a buffer that is too large and one unable to hold anything larger than
an integer. The alignment requirement is optional instead and by default such
that it's the most stringent (the largest) for any object whose size is at most
equal to the one provided.<br/>
It's worth noting that providing a size of 0 (which is an accepted value in all
respects) will force the system to dynamically allocate the contained objects in
all cases.

212
docs/md/process.md Normal file
View File

@ -0,0 +1,212 @@
# Crash Course: cooperative scheduler
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The process](#the-process)
* [Adaptor](#adaptor)
* [The scheduler](#the-scheduler)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Sometimes processes are a useful tool to work around the strict definition of a
system and introduce logic in a different way, usually without resorting to the
introduction of other components.
`EnTT` offers a minimal support to this paradigm by introducing a few classes
that users can use to define and execute cooperative processes.
# The process
A typical process must inherit from the `process` class template that stays true
to the CRTP idiom. Moreover, derived classes must specify what's the intended
type for elapsed times.
A process should expose publicly the following member functions whether needed
(note that it isn't required to define a function unless the derived class wants
to _override_ the default behavior):
* `void update(Delta, void *);`
It's invoked once per tick until a process is explicitly aborted or it
terminates either with or without errors. Even though it's not mandatory to
declare this member function, as a rule of thumb each process should at
least define it to work properly. The `void *` parameter is an opaque pointer
to user data (if any) forwarded directly to the process during an update.
* `void init();`
It's invoked when the process joins the running queue of a scheduler. This
happens as soon as it's attached to the scheduler if the process is a top
level one, otherwise when it replaces its parent if the process is a
continuation.
* `void succeeded();`
It's invoked in case of success, immediately after an update and during the
same tick.
* `void failed();`
It's invoked in case of errors, immediately after an update and during the
same tick.
* `void aborted();`
It's invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, this depends solely on whether the
process is aborted immediately or not.
Derived classes can also change the internal state of a process by invoking
`succeed` and `fail`, as well as `pause` and `unpause` the process itself. All
these are protected member functions made available to be able to manage the
life cycle of a process from a derived class.
Here is a minimal example for the sake of curiosity:
```cpp
struct my_process: entt::process<my_process, std::uint32_t> {
using delta_type = std::uint32_t;
my_process(delta_type delay)
: remaining{delay}
{}
void update(delta_type delta, void *) {
remaining -= std::min(remaining, delta);
// ...
if(!remaining) {
succeed();
}
}
private:
delta_type remaining;
};
```
## Adaptor
Lambdas and functors can't be used directly with a scheduler for they are not
properly defined processes with managed life cycles.<br/>
This class helps in filling the gap and turning lambdas and functors into
full-featured processes usable by a scheduler.
The function call operator has a signature similar to the one of the `update`
function of a process but for the fact that it receives two extra arguments to
call whenever a process is terminated with success or with an error:
```cpp
void(Delta delta, void *data, auto succeed, auto fail);
```
Parameters have the following meaning:
* `delta` is the elapsed time.
* `data` is an opaque pointer to user data if any, `nullptr` otherwise.
* `succeed` is a function to call when a process terminates with success.
* `fail` is a function to call when a process terminates with errors.
Both `succeed` and `fail` accept no parameters at all.
Note that usually users shouldn't worry about creating adaptors at all. A
scheduler creates them internally each and every time a lambda or a functor is
used as a process.
# The scheduler
A cooperative scheduler runs different processes and helps managing their life
cycles.
Each process is invoked once per tick. If it terminates, it's removed
automatically from the scheduler and it's never invoked again. Otherwise it's
a good candidate to run one more time the next tick.<br/>
A process can also have a child. In this case, the parent process is replaced
with its child when it terminates and only if it returns with success. In case
of errors, both the parent process and its child are discarded. This way, it's
easy to create chain of processes to run sequentially.
Using a scheduler is straightforward. To create it, users must provide only the
type for the elapsed times and no arguments at all:
```cpp
entt::scheduler<std::uint32_t> scheduler;
```
It has member functions to query its internal data structures, like `empty` or
`size`, as well as a `clear` utility to reset it to a clean state:
```cpp
// checks if there are processes still running
const auto empty = scheduler.empty();
// gets the number of processes still running
entt::scheduler<std::uint32_t>::size_type size = scheduler.size();
// resets the scheduler to its initial state and discards all the processes
scheduler.clear();
```
To attach a process to a scheduler there are mainly two ways:
* If the process inherits from the `process` class template, it's enough to
indicate its type and submit all the parameters required to construct it to
the `attach` member function:
```cpp
scheduler.attach<my_process>(1000u);
```
* Otherwise, in case of a lambda or a functor, it's enough to provide an
instance of the class to the `attach` member function:
```cpp
scheduler.attach([](auto...){ /* ... */ });
```
In both cases, the return value is an opaque object that offers a `then` member
function to use to create chains of processes to run sequentially.<br/>
As a minimal example of use:
```cpp
// schedules a task in the form of a lambda function
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
// ...
})
// appends a child in the form of another lambda function
.then([](auto delta, void *, auto succeed, auto fail) {
// ...
})
// appends a child in the form of a process class
.then<my_process>(1000u);
```
To update a scheduler and therefore all its processes, the `update` member
function is the way to go:
```cpp
// updates all the processes, no user data are provided
scheduler.update(delta);
// updates all the processes and provides them with custom data
scheduler.update(delta, &data);
```
In addition to these functions, the scheduler offers an `abort` member function
that can be used to discard all the running processes at once:
```cpp
// aborts all the processes abruptly ...
scheduler.abort(true);
// ... or gracefully during the next tick
scheduler.abort();
```

75
docs/md/reference.md Normal file
View File

@ -0,0 +1,75 @@
# Similar projects
There are many projects similar to `EnTT`, both open source and not.<br/>
Some even borrowed some ideas from this library and expressed them in different
languages.<br/>
Others developed different architectures from scratch and therefore offer
alternative solutions with their pros and cons.
Below an incomplete list of those that I've come across so far.<br/>
If some terms or designs aren't clear, I recommend referring to the
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
details.
I hope this list can grow much more in the future:
* C:
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
on sparse sets.
* [Diana](https://github.com/discoloda/Diana): an ECS that uses sparse sets to
keep track of entities in systems.
* [Flecs](https://github.com/SanderMertens/flecs): a multithreaded archetype
ECS based on semi-contiguous arrays rather than chunks.
* [lent](https://github.com/nem0/lent): the Donald Trump of the ECS libraries.
* C++:
* [decs](https://github.com/vblanco20-1/decs): a chunk based archetype ECS.
* [ecst](https://github.com/SuperV1234/ecst): a multithreaded compile-time
ECS that uses sparse sets to keep track of entities in systems.
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
uses a single large matrix of components indexed with entities.
* [Gaia-ECS](https://github.com/richardbiely/gaia-ecs): a chunk based
archetype ECS.
* [Polypropylene](https://github.com/pmbittner/Polypropylene): a hybrid
solution between an ECS and dynamic mixins.
* C#
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
C# and Unity, where _reactive systems_ were invented.
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
Component System framework.
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
platform agnostic and table based ECS framework.
* Go:
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
by `EnTT`.
* Javascript:
* [\@javelin/ecs](https://github.com/3mcd/javelin/tree/master/packages/ecs):
an archetype ECS in TypeScript.
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
investigate the underlying design of `ecsy` but it looks cool anyway.
* Perl:
* [Game::Entities](https://gitlab.com/jjatria/perl-game-entities): a simple
entity registry for ECS designs inspired by `EnTT`.
* Raku:
* [Game::Entities](https://gitlab.com/jjatria/raku-game-entities): a simple
entity registry for ECS designs inspired by `EnTT`.
* Rust:
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS
written in Rust.
* [Specs](https://github.com/amethyst/specs): a parallel ECS based mainly on
hierarchical bitsets that allows different types of storage as needed.
* Zig
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
If you know of other resources out there that can be of interest for the reader,
feel free to open an issue or a PR and I'll be glad to add them to this page.

191
docs/md/resource.md Normal file
View File

@ -0,0 +1,191 @@
# Crash Course: resource management
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
* [Resource handle](#resource-handle)
* [Loaders](#loader)
* [The cache class](#the-cache)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Resource management is usually one of the most critical parts of a game.
Solutions are often tuned to the particular application. There exist several
approaches and all of them are perfectly fine as long as they fit the
requirements of the piece of software in which they are used.<br/>
Examples are loading everything on start, loading on request, predictive
loading, and so on.
`EnTT` doesn't pretend to offer a _one-fits-all_ solution for the different
cases.<br/>
Instead, the library comes with a minimal, general purpose resource cache that
might be useful in many cases.
# The resource, the loader and the cache
Resource, loader and cache are the three main actors for the purpose.<br/>
The _resource_ is an image, an audio, a video or any other type:
```cpp
struct my_resource { const int value; };
```
The _loader_ is a callable type the aim of which is to load a specific resource:
```cpp
struct my_loader final {
using result_type = std::shared_ptr<my_resource>;
result_type operator()(int value) const {
// ...
return std::make_shared<my_resource>(value);
}
};
```
Its function operator can accept any arguments and should return a value of the
declared result type (`std::shared_ptr<my_resource>` in the example).<br/>
A loader can also overload its function call operator to make it possible to
construct the same or another resource from different lists of arguments.
Finally, a cache is a specialization of a class template tailored to a specific
resource and (optionally) a loader:
```cpp
using my_cache = entt::resource_cache<my_resource, my_loader>;
// ...
my_cache cache{};
```
The class is designed to create different caches for different resource types
and to manage each one independently in the most appropriate way.<br/>
As a (very) trivial example, audio tracks can survive in most of the scenes of
an application while meshes can be associated with a single scene only, then
discarded when a player leaves it.
## Resource handle
Resources aren't returned directly to the caller. Instead, they are wrapped in a
_resource handle_, an instance of the `entt::resource` class template.<br/>
For those who know the _flyweight design pattern_ already, that's exactly what
it is. To all others, this is the time to brush up on some notions instead.
A shared pointer could have been used as a resource handle. In fact, the default
implementation mostly maps the interface of its standard counterpart and only
adds a few things on top of it.<br/>
However, the handle in `EnTT` is designed as a standalone class template. This
is due to the fact that specializing a class in the standard library is often
undefined behavior while having the ability to specialize the handle for one,
more or all resource types could help over time.
## Loaders
A loader is responsible for _loading_ resources (quite obviously).<br/>
By default, it's just a callable object that forwards its arguments to the
resource itself. That is, a _passthrough type_. All the work is demanded to the
constructor(s) of the resource itself.<br/>
Loaders also are fully customizable as expected.
A custom loader is a class with at least one function call operator and a member
type named `result_type`.<br/>
The loader isn't required to return a resource handle. As long as `return_type`
is suitable for constructing a handle, that's fine.
When using the default handle, it expects a resource type which is convertible
to or suitable for constructing an `std::shared_ptr<Type>` (where `Type` is the
actual resource type).<br/>
In other terms, the loader should return shared pointers to the given resource
type. However, this isn't mandatory. Users can easily get around this constraint
by specializing both the handle and the loader.
A cache forwards all its arguments to the loader if required. This means that
loaders can also support tag dispatching to offer different loading policies:
```cpp
struct my_loader {
using result_type = std::shared_ptr<my_resource>;
struct from_disk_tag{};
struct from_network_tag{};
template<typename Args>
result_type operator()(from_disk_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
}
template<typename Args>
result_type operator()(from_network_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
}
}
```
This makes the whole loading logic quite flexible and easy to extend over time.
## The cache class
The cache is the class that is asked to _connect the dots_.<br/>
It loads the resources, stores them aside and returns handles as needed:
```cpp
entt::resource_cache<my_resource, my_loader> cache{};
```
Under the hood, a cache is nothing more than a map where the key value has type
`entt::id_type` while the mapped value is whatever type its loader returns.<br/>
For this reason, it offers most of the functionalities a user would expect from
a map, such as `empty` or `size` and so on. Similarly, it's an iterable type
that also supports indexing by resource id:
```cpp
for(auto [id, res]: cache) {
// ...
}
if(entt::resource<my_resource> res = cache["resource/id"_hs]; res) {
// ...
}
```
Please, refer to the inline documentation for all the details about the other
functions (such as `contains` or `erase`).
Set aside the part of the API that this class _shares_ with a map, it also adds
something on top of it in order to address the most common requirements of a
resource cache.<br/>
In particular, it doesn't have an `emplace` member function which is replaced by
`load` and `force_load` instead (where the former loads a new resource only if
not present while the second triggers a forced loading in any case):
```cpp
auto ret = cache.load("resource/id"_hs);
// true only if the resource was not already present
const bool loaded = ret.second;
// takes the resource handle pointed to by the returned iterator
entt::resource<my_resource> res = ret.first->second;
```
Note that the hashed string is used for convenience in the example above.<br/>
Resource identifiers are nothing more than integral values. Therefore, plain
numbers as well as non-class enum value are accepted.
It's worth mentioning that the iterators of a cache as well as its indexing
operators return resource handles rather than instances of the mapped type.<br/>
Since the cache has no control over the loader and a resource isn't required to
also be convertible to bool, these handles can be invalid. This usually means an
error in the user logic but it may also be an _expected_ event.<br/>
It's therefore recommended to verify handles validity with a check in debug (for
example, when loading) or an appropriate logic in retail.

549
docs/md/signal.md Normal file
View File

@ -0,0 +1,549 @@
# Crash Course: events, signals and everything in between
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Delegate](#delegate)
* [Runtime arguments](#runtime-arguments)
* [Lambda support](#lambda-support)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Signals are more often than not a core part of games and software architectures
in general.<br/>
They help to decouple the various parts of a system while allowing them to
communicate with each other somehow.
The so called _modern C++_ comes with a tool that can be useful in this regard,
the `std::function`. As an example, it can be used to create delegates.<br/>
However, there is no guarantee that an `std::function` doesn't perform
allocations under the hood and this could be problematic sometimes. Furthermore,
it solves a problem but may not adapt well to other requirements that may arise
from time to time.
In case that the flexibility and power of an `std::function` isn't required or
if the price to pay for them is too high, `EnTT` offers a complete set of
lightweight classes to solve the same and many other problems.
# Delegate
A delegate can be used as a general purpose invoker with no memory overhead for
free functions and member functions provided along with an instance on which to
invoke them.<br/>
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
expect to use it whenever an `std::function` fits well. That said, it's most
likely even a better fit than an `std::function` in a lot of cases, so expect to
use it quite a lot anyway.
The interface is trivial. It offers a default constructor to create empty
delegates:
```cpp
entt::delegate<int(int)> delegate{};
```
What is needed to create an instance is to specify the type of the function the
delegate _accepts_, that is the signature of the functions it models.<br/>
However, attempting to use an empty delegate by invoking its function call
operator results in undefined behavior or most likely a crash.
There exist a few overloads of the `connect` member function to initialize a
delegate:
```cpp
int f(int i) { return i; }
struct my_struct {
int f(const int &i) const { return i; }
};
// bind a free function to the delegate
delegate.connect<&f>();
// bind a member function to the delegate
my_struct instance;
delegate.connect<&my_struct::f>(instance);
```
The delegate class also accepts data members, if needed. In this case, the
function type of the delegate is such that the parameter list is empty and the
value of the data member is at least convertible to the return type.
Free functions having type equivalent to `void(T &, args...)` are accepted as
well. The first argument `T &` is considered a payload and the function will
receive it back every time it's invoked. In other terms, this works just fine
with the above definition:
```cpp
void g(const char &c, int i) { /* ... */ }
const char c = 'c';
delegate.connect<&g>(c);
delegate(42);
```
The function `g` is invoked with a reference to `c` and `42`. However, the
function type of the delegate is still `void(int)`. This is also the signature
of its function call operator.<br/>
Another interesting aspect of the delegate class is that it accepts functions
with a list of parameters that is shorter than that of its function type:
```cpp
void g() { /* ... */ }
delegate.connect<&g>();
delegate(42);
```
Where the function type of the delegate is `void(int)` as above. It goes without
saying that the extra arguments are silently discarded internally.<br/>
This is a nice-to-have feature in a lot of cases, as an example when the
`delegate` class is used as a building block of a signal-slot system.
To create and initialize a delegate at once, there are a few specialized
constructors. Because of the rules of the language, the listener is provided by
means of the `entt::connect_arg` variable template:
```cpp
entt::delegate<int(int)> func{entt::connect_arg<&f>};
```
Aside `connect`, a `disconnect` counterpart isn't provided. Instead, there
exists a `reset` member function to use to clear a delegate.<br/>
To know if a delegate is empty, it can be used explicitly in every conditional
statement:
```cpp
if(delegate) {
// ...
}
```
Finally, to invoke a delegate, the function call operator is the way to go as
already shown in the examples above:
```cpp
auto ret = delegate(42);
```
In all cases, listeners don't have to strictly follow the signature of the
delegate. As long as a listener can be invoked with the given arguments to yield
a result that is convertible to the given result type, everything works just
fine.
As a side note, members of classes may or may not be associated with instances.
If they are not, the first argument of the function type must be that of the
class on which the members operate and an instance of this class must obviously
be passed when invoking the delegate:
```cpp
entt::delegate<void(my_struct &, int)> delegate;
delegate.connect<&my_struct::f>();
my_struct instance;
delegate(instance, 42);
```
In this case, it's not possible to _deduce_ the function type since the first
argument doesn't necessarily have to be a reference (for example, it can be a
pointer, as well as a const reference).<br/>
Therefore, the function type must be declared explicitly for unbound members.
## Runtime arguments
The `delegate` class is meant to be used primarily with template arguments.
However, as a consequence of its design, it also offers minimal support for
runtime arguments.<br/>
When used like this, some features aren't supported though. In particular:
* Curried functions aren't accepted.
* Functions with an argument list that differs from that of the delegate aren't
supported.
* Return type and types of arguments **must** coincide with those of the
delegate and _being at least convertible_ isn't enough anymore.
Moreover, for a given function type `Ret(Args...)`, the signature of the
functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
Runtime arguments can be passed both to the constructor of a delegate and to the
`connect` member function. An optional parameter is also accepted in both cases.
This argument is used to pass arbitrary user data back and forth as a
`const void *` upon invocation.<br/>
To connect a function to a delegate _in the hard way_:
```cpp
int func(const void *ptr, int i) { return *static_cast<const int *>(ptr) * i; }
const int value = 42;
// use the constructor ...
entt::delegate delegate{&func, &value};
// ... or the connect member function
delegate.connect(&func, &value);
```
The type of the delegate is deduced from the function if possible. In this case,
since the first argument is an implementation detail, the deduced function type
is `int(int)`.<br/>
Invoking a delegate built in this way follows the same rules as previously
explained.
## Lambda support
In general, the `delegate` class doesn't fully support lambda functions in all
their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
replacement for an `std::function`. Instead, it tries to overcome the problems
with the latter.<br/>
That being said, non-capturing lambda functions are supported, even though some
features aren't available in this case.
This is a logical consequence of the support for connecting functions at
runtime. Therefore, lambda functions undergo the same rules and
limitations.<br/>
In fact, since non-capturing lambda functions decay to pointers to functions,
they can be used with a `delegate` as if they were _normal functions_ with
optional payload:
```cpp
my_struct instance;
// use the constructor ...
entt::delegate delegate{+[](const void *ptr, int value) {
return static_cast<const my_struct *>(ptr)->f(value);
}, &instance};
// ... or the connect member function
delegate.connect([](const void *ptr, int value) {
return static_cast<const my_struct *>(ptr)->f(value);
}, &instance);
```
As above, the first parameter (`const void *`) isn't part of the function type
of the delegate and is used to dispatch arbitrary user data back and forth. In
other terms, the function type of the delegate above is `int(int)`.
# Signals
Signal handlers work with references to classes, function pointers and pointers
to members. Listeners can be any kind of objects and users are in charge of
connecting and disconnecting them from a signal to avoid crashes due to
different lifetimes. On the other side, performance shouldn't be affected that
much by the presence of such a signal handler.<br/>
Signals make use of delegates internally and therefore they undergo the same
rules and offer similar functionalities. It may be a good idea to consult the
documentation of the `delegate` class for further information.
A signal handler is can be used as a private data member without exposing any
_publish_ functionality to the clients of a class.<br/>
The basic idea is to impose a clear separation between the signal itself and the
`sink` class, that is a tool to be used to connect and disconnect listeners on
the fly.
The API of a signal handler is straightforward. If a collector is supplied to
the signal when something is published, all the values returned by its listeners
are literally _collected_ and used later by the caller. Otherwise, the class
works just like a plain signal that emits events from time to time.<br/>
To create instances of signal handlers it's sufficient to provide the type of
function to which they refer:
```cpp
entt::sigh<void(int, char)> signal;
```
Signals offer all the basic functionalities required to know how many listeners
they contain (`size`) or if they contain at least a listener (`empty`), as well
as a function to use to swap handlers (`swap`).
Besides them, there are member functions to use both to connect and disconnect
listeners in all their forms by means of a sink:
```cpp
void foo(int, char) { /* ... */ }
struct listener {
void bar(const int &, char) { /* ... */ }
};
// ...
entt::sink sink{signal};
listener instance;
sink.connect<&foo>();
sink.connect<&listener::bar>(instance);
// ...
// disconnects a free function
sink.disconnect<&foo>();
// disconnect a member function of an instance
sink.disconnect<&listener::bar>(instance);
// disconnect all member functions of an instance, if any
sink.disconnect(instance);
// discards all listeners at once
sink.disconnect();
```
As shown above, listeners don't have to strictly follow the signature of the
signal. As long as a listener can be invoked with the given arguments to yield a
result that is convertible to the given return type, everything works just
fine.<br/>
It's also possible to connect a listener before other elements already contained
by the signal. The `before` function returns a `sink` object that is correctly
initialized for the purpose and can be used to connect one or more listeners in
order and in the desired position:
```cpp
sink.before<&foo>().connect<&listener::bar>(instance);
```
In all cases, the `connect` member function returns by default a `connection`
object to be used as an alternative to break a connection by means of its
`release` member function.<br/>
A `scoped_connection` can also be created from a connection. In this case, the
link is broken automatically as soon as the object goes out of scope.
Once listeners are attached (or even if there are no listeners at all), events
and data in general are published through a signal by means of the `publish`
member function:
```cpp
signal.publish(42, 'c');
```
To collect data, the `collect` member function is used instead:
```cpp
int f() { return 0; }
int g() { return 1; }
// ...
entt::sigh<int()> signal;
entt::sink sink{signal};
sink.connect<&f>();
sink.connect<&g>();
std::vector<int> vec{};
signal.collect([&vec](int value) { vec.push_back(value); });
assert(vec[0] == 0);
assert(vec[1] == 1);
```
A collector must expose a function operator that accepts as an argument a type
to which the return type of the listeners can be converted. Moreover, it can
optionally return a boolean value that is true to stop collecting data, false
otherwise. This way one can avoid calling all the listeners in case it isn't
necessary.<br/>
Functors can also be used in place of a lambda. Since the collector is copied
when invoking the `collect` member function, `std::ref` is the way to go in this
case:
```cpp
struct my_collector {
std::vector<int> vec{};
bool operator()(int v) {
vec.push_back(v);
return true;
}
};
// ...
my_collector collector;
signal.collect(std::ref(collector));
```
# Event dispatcher
The event dispatcher class allows users to trigger immediate events or to queue
and publish them all together later.<br/>
This class lazily instantiates its queues. Therefore, it's not necessary to
_announce_ the event types in advance:
```cpp
// define a general purpose dispatcher
entt::dispatcher dispatcher{};
```
A listener registered with a dispatcher is such that its type offers one or more
member functions that take arguments of type `Event &` for any type of event,
regardless of the return value.<br/>
These functions are linked directly via `connect` to a _sink_:
```cpp
struct an_event { int value; };
struct another_event {};
struct listener {
void receive(const an_event &) { /* ... */ }
void method(const another_event &) { /* ... */ }
};
// ...
listener listener;
dispatcher.sink<an_event>().connect<&listener::receive>(listener);
dispatcher.sink<another_event>().connect<&listener::method>(listener);
```
Note that connecting listeners within event handlers can result in undefined
behavior.<br/>
The `disconnect` member function is used to remove one listener at a time or all
of them at once:
```cpp
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
dispatcher.sink<another_event>().disconnect(listener);
```
The `trigger` member function serves the purpose of sending an immediate event
to all the listeners registered so far:
```cpp
dispatcher.trigger(an_event{42});
dispatcher.trigger<another_event>();
```
Listeners are invoked immediately, order of execution isn't guaranteed. This
method can be used to push around urgent messages like an _is terminating_
notification on a mobile app.
On the other hand, the `enqueue` member function queues messages together and
helps to maintain control over the moment they are sent to listeners:
```cpp
dispatcher.enqueue<an_event>(42);
dispatcher.enqueue(another_event{});
```
Events are stored aside until the `update` member function is invoked:
```cpp
// emits all the events of the given type at once
dispatcher.update<an_event>();
// emits all the events queued so far at once
dispatcher.update();
```
This way users can embed the dispatcher in a loop and literally dispatch events
once per tick to their systems.
## Named queues
All queues within a dispatcher are associated by default with an event type and
then retrieved from it.<br/>
However, it's possible to create queues with different _names_ (and therefore
also multiple queues for a single type). In fact, more or less all functions
also take an additional parameter. As an example:
```cpp
dispatcher.sink<an_event>("custom"_hs).connect<&listener::receive>(listener);
```
In this case, the term _name_ is misused as these are actual numeric identifiers
of type `id_type`.<br/>
An exception to this rule is the `enqueue` function. There is no additional
parameter for it but rather a different function:
```cpp
dispatcher.enqueue_hint<an_event>("custom"_hs, 42);
```
This is mainly due to the template argument deduction rules and unfortunately
there is no real (elegant) way to avoid it.
# Event emitter
A general purpose event emitter thought mainly for those cases where it comes to
working with asynchronous stuff.<br/>
Originally designed to fit the requirements of
[`uvw`](https://github.com/skypjack/uvw) (a wrapper for `libuv` written in
modern C++), it was adapted later to be included in this library.
To create an emitter type, derived classes must inherit from the base as:
```cpp
struct my_emitter: emitter<my_emitter> {
// ...
}
```
Handlers for the different events are created internally on the fly. It's not
required to specify in advance the full list of accepted events.<br/>
Moreover, whenever an event is published, an emitter also passes a reference
to itself to its listeners.
To create new instances of an emitter, no arguments are required:
```cpp
my_emitter emitter{};
```
Listeners are movable and callable objects (free functions, lambdas, functors,
`std::function`s, whatever) whose function type is compatible with:
```cpp
void(Type &, my_emitter &)
```
Where `Type` is the type of event they want to receive.<br/>
To attach a listener to an emitter, there exists the `on` member function:
```cpp
emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
// ...
});
```
Similarly, the `reset` member function is used to disconnect listeners given a
type while `clear` is used to disconnect all listeners at once:
```cpp
// resets the listener for my_event
emitter.erase<my_event>();
// resets all listeners
emitter.clear()
```
To send an event to the listener registered on a given type, the `publish`
function is the way to go:
```cpp
struct my_event { int i; };
// ...
emitter.publish(my_event{42});
```
Finally, the `empty` member function tests if there exists at least a listener
registered with the event emitter while `contains` is used to check if a given
event type is associated with a valid listener:
```cpp
if(emitter.contains<my_event>()) {
// ...
}
```
This class introduces a _nice-to-have_ model based on events and listeners.<br/>
More in general, it's a handy tool when the derived classes _wrap_ asynchronous
operations but it's not limited to such uses.

107
docs/md/unreal.md Normal file
View File

@ -0,0 +1,107 @@
# EnTT and Unreal Engine
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Enable Cpp17](#enable-cpp17)
* [EnTT as a third party module](#entt-as-a-third-party-module)
* [Include EnTT](#include-entt)
<!--
@endcond TURN_OFF_DOXYGEN
-->
## Enable Cpp17
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
is C++14.<br/>
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in
the main module of the project there should be a `<Game Name>.Build.cs` file,
the constructor of which must contain the following lines:
```cs
PCHUsage = PCHUsageMode.NoSharedPCHs;
PrivatePCHHeaderFile = "<PCH filename>.h";
CppStandard = CppStandardVersion.Cpp17;
```
Replace `<PCH filename>.h` with the name of the already existing PCH header
file, if any.<br/>
In case the project doesn't already contain a file of this type, it's possible
to create one with the following content:
```cpp
#pragma once
#include "CoreMinimal.h"
```
Remember to remove any old `PCHUsage = <...>` line that was previously there. At
this point, C++17 support should be in place.<br/>
Try to compile the project to ensure it works as expected before following
further steps.
Note that updating a *project* to C++17 doesn't necessarily mean that the IDE in
use will also start to recognize its syntax.<br/>
If the plan is to use C++17 in the project too, check the specific instructions
for the IDE in use.
## EnTT as a third party module
Once this point is reached, the `Source` directory should look like this:
```
Source
| MyGame.Target.cs
| MyGameEditor.Target.cs
|
+---MyGame
| | MyGame.Build.cs
| | MyGame.h (PCH Header file)
|
\---ThirdParty
\---EnTT
| EnTT.Build.cs
|
\---entt (GitHub repository content inside)
```
To make this happen, create the folder `ThirdParty` under `Source` if it doesn't
exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
Within the latter, create a new file `EnTT.Build.cs` with the following content:
```cs
using System.IO;
using UnrealBuildTool;
public class EnTT: ModuleRules {
public EnTT(ReadOnlyTargetRules Target) : base(Target) {
Type = ModuleType.External;
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "entt", "src", "entt"));
}
}
```
The last line indicates that the actual files will be found in the directory
`EnTT/entt/src/entt`.<br/>
Download the repository for `EnTT` and place it next to `EnTT.Build.cs` or
update the path above accordingly.
Finally, open the `<Game Name>.Build.cs` file and add `EnTT` as a dependency at
the end of the list:
```cs
PublicDependencyModuleNames.AddRange(new[] {
"Core", "CoreUObject", "Engine", "InputCore", [...], "EnTT"
});
```
Note that some IDEs might require a restart to start recognizing the new module
for code-highlighting features and such.
## Include EnTT
In any source file of the project, add `#include "entt.hpp"` or any other path
to the file from `EnTT` to use it.<br/>
Try to create a registry as `entt::registry registry;` to make sure everything
compiles fine.