Skip to content

Commit

Permalink
WIP: full documenting the framework
Browse files Browse the repository at this point in the history
  • Loading branch information
skypjack committed Feb 20, 2018
1 parent b46e4fa commit b87f639
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 33 deletions.
188 changes: 174 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1300,27 +1300,28 @@ 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.

The `EnTT` framework doesn't pretend to offer a _one fits all_ solution for the
The `EnTT` framework doesn't pretend to offer a _one-fits-all_ solution for the
different cases. Instead, it offers a minimal and perhaps trivial cache that can
be useful most of the time during prototyping and sometimes even in a production
environment.<br/>
For those interested in the subject, the plan is to improve it considerably over
time both in terms of performance and in terms of functionalities. Hoping to
make it, of course, one step at a time.
time in terms of performance, memory usage and functionalities. Hoping to make
it, of course, one step at a time.

## The resource, the loader and the cache

There are three main actors in the model.
There are three main actors in the model: the resource, the loader and the
cache.

The _resource_ is whatever the user wants it to be. An image, a video, an audio,
whatever. There are no limits.<br/>
As a minimal example:

```cpp
struct MyResource {};
struct MyResource { const int value; };
```
A _loader_ is a class the aim of which is to load a specific resource. It must
A _loader_ is a class the aim of which is to load a specific resource. It has to
inherit directly from the dedicated base class as in the following example:
```cpp
Expand All @@ -1331,15 +1332,15 @@ struct MyLoader final: public entt::ResourceLoader<MyLoader, MyResource> {

Where `MyResource` is the type of resources it creates.<br/>
A resource loader must also expose a public, const member function named `load`
that accepts a variable number of arguments and returns a shared pointer to the
resource just created.<br/>
that accepts a variable number of arguments and returns a shared pointer to a
resource.<br/>
As an example:

```cpp
struct MyLoader: entt::ResourceLoader<MyLoader, MyResource> {
std::shared_ptr<MyResource> load(int) const {
// use the integer value somehow
return std::make_shared<MyResource>();
std::shared_ptr<MyResource> load(int value) const {
// ...
return std::shared_ptr<MyResource>(new MyResource{ value });
}
};
```
Expand All @@ -1351,11 +1352,170 @@ current implementation. One could argue that a cache can easily work with
loaders of any type. However, future changes won't be breaking ones by forcing
the use of a base class today and that's why the model is already in its place.
TODO
Finally, a cache is a specialization of a class template tailored to a specific
resource:
```cpp
using MyResourceCache = entt::ResourceCache<MyResource>;
// ...
MyResourceCache cache{};
```

The idea is to create different caches for different types of resources and to
manage each of them independently and 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 and then
discarded when the user leaves it.

A cache offers a set of basic functionalities to query its internal state and to
_organize_ it:

```cpp
// gets the number of resources managed by a cache
auto size = cache.size();

// checks if a cache contains at least a valid resource
auto empty = cache.empty();

// clears a cache and discards its content
cache.clear();
```

Besides these member functions, it contains what is needed to load, use and
discard resources of the given type.<br/>
Before to explore this part of the interface, it makes sense to mention how
resources are identified. The type of the identifiers to use is defined as:

```cpp
entt::ResourceCache<Resource>::resource_type
```

Where `resource_type` is an alias for `entt::HashedString`. Therefore, resource
identifiers are created explicitly as in the following example:

```cpp
constexpr auto identifier = entt::HashedString{"my/resource/identifier"};
```
The class HashedString is described in a dedicated section, so I won't do in
details here.
Resources are loaded and thus stored in a cache through the `load` member
function. It accepts the loader to use as a template parameter, the resource
identifier and the parameters used to construct the resource as arguments:
```cpp
// uses the identifier declared above
cache.load<MyLoader>(identifier, 0);
// uses a const char * directly as an identifier
cache.load<MyLoader>("another/identifier", 42);
```

The return value can be used to know if the resource has been loaded correctly.
In case the loader returns an invalid pointer or the resource already exists in
the cache, a false value is returned:

```cpp
if(!cache.load<MyLoader>("another/identifier", 42)) {
// ...
}
```

Unfortunately, in this case there is no way to know what was the problem
exactly. However, before trying to load a resource or after an error, one can
use the `contains` member function to know if a cache already contains a
specific resource:

```cpp
auto exists = cache.contains("my/identifier");
```

There exists also a member function to use to force a reload of an already
existing resource if needed:

## Tiny handle
```cpp
auto result = cache.reload<MyLoader>("another/identifier", 42);
```

As above, the function returns true in case of success, false otherwise. The
sole difference in this case is that an error necessarily means that the loader
has failed for some reasons to load the resource.<br/>
Note that the `reload` member function is a kind of alias of the following
snippet:

```cpp
cache.discard(identifier);
cache.load<MyLoader>(identifier, 42);
```

In this case, the `discard` member function is used to get rid of a resource if
loaded. In case the cache doesn't contain a resource for the given identifier,
the function does nothing and returns immediately.

So far, so good. Resources are finally loaded and stored within the cache.<br/>
They are returned to the users in the form of handles. To get one of them:

```cpp
auto handle = cache.handle("my/identifier");
```

The idea behind a handle is the same of the flyweight pattern. In other terms,
resources aren't copied around. Instead, instances are shared between handles.
Users of a resource owns a handle and it guarantees that a resource isn't
destroyed until all the handles are destroyed, even if the resource itself is
removed from the cache.<br/>
Handles are tiny objects both movable and copyable. They returns the contained
resource as a const reference on request:

* By means of the `get` member function:

```cpp
const auto &resource = handle.get();
```

* Using the proper cast operator:

```cpp
const auto &resource = handle;
```

* Through the dereference operator:

```cpp
const auto &resource = *handle;
```

The resource can also be accessed directly using the arrow operator if required:

```cpp
auto value = handle->value;
```

To test if a handle is still valid, the cast operator to `bool` allows the users
to use it in a guard:

```cpp
if(handle) {
// ...
}
```

Finally, in case there is the need to load a resource and thus to get a handle
without storing the resource itself in the cache, users can rely on the `temp`
member function template.<br/>
The declaration is similar to the one of the one of `load` but for the fact that
it doesn't return a boolean value. Instead, it returns a (possibly invalid)
handle for the resource:

```cpp
auto handle = cache.temp<MyLoader>("another/identifier", 42);
```

TODO
Do not forget to test the handle for validity. Otherwise, getting the reference
to the resource it points may result in undefined behavior.

# Crash Course: events, signals and everything in between

Expand Down
17 changes: 1 addition & 16 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,6 @@
* to analyze, long term feature: systems organizer based on dependency graphs for implicit parallelism (I don't want to think anymore in future :-))
* save/restore functionalities - see #27

* blueprint registry to manage object templates when creating entities, something along this line:

template<typename R>
struct BlueprintRegistry: public R {
template<typename... Component> void blueprint(); // register a blueprint
template<typename... Component> void create(); // use the underlying create and looks for a blueprint if any
};

It will help creating entities with predefined sets of components and getting rid of the annoying factories all around the codebase.
* parent-child relationships between entities directly managed by the registry. is it possible to do that in a clean and safe way?

* AOB

---> candidate
we can get rid of the available vector and use an entt_traits::entity_type element and a boolean in place of it.
the idea is to create an implicit next-list in the entt_traits::entity_mask part of an entity, so that it doesn't affect the version.
the each member function can the iterate all the entities and check if the mask return the entity position to test for validity.
in all the other cases (mask different from the position or mask equal to the available start point) the entity isn't valid.
it sounds good indeed, experimental feature to be tested.
4 changes: 2 additions & 2 deletions src/entt/resource/cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ class ResourceCache {
* @return True if the resource is ready to use, false otherwise.
*/
template<typename Loader, typename... Args>
void reload(resource_type id, Args&&... args) {
return (discard(id), load(id, std::forward<Args>(args)...));
bool reload(resource_type id, Args&&... args) {
return (discard(id), load<Loader>(id, std::forward<Args>(args)...));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/mod/mod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <cassert>
#include <map>
#include <string>
#include <duktape.h>
#include <entt/entity/registry.hpp>
#include "duktape.h"

template<typename Type>
struct tag { using type = Type; };
Expand Down

0 comments on commit b87f639

Please sign in to comment.