doc: meta, container support

This commit is contained in:
Michele Caini
2020-06-18 10:17:50 +02:00
parent b919b172b1
commit 0c777e10e5
2 changed files with 151 additions and 54 deletions

54
TODO
View File

@@ -27,56 +27,6 @@ Next:
* WIP:
- meta: update doc
- static constexpr -> inline constexpr
- test: meta type seq/assoc traits, containers
- document meta type traits
- remove internal::find_if
struct meta_view_node {
using size_type = std::size_t;
meta_type_node *(* const key_type)() ENTT_NOEXCEPT;
meta_type_node *(* const value_type)() ENTT_NOEXCEPT;
void(* const next)(meta_handle) ENTT_NOEXCEPT;
void(* const prev)(meta_handle) ENTT_NOEXCEPT;
bool(* const empty)(meta_handle) ENTT_NOEXCEPT;
void(* const clear)(meta_handle);
size_type(* const size)(meta_handle) ENTT_NOEXCEPT;
meta_any(* const begin)(meta_handle) ENTT_NOEXCEPT;
meta_any(* const end)(meta_handle) ENTT_NOEXCEPT;
meta_any(* const erase)(meta_handle, meta_handle);
meta_any(* const insert)(meta_handle, meta_any, meta_any);
bool(* const set)(meta_handle, meta_any, meta_any);
meta_any(* const get)(meta_handle, meta_any);
};
static meta_view_node * view() ENTT_NOEXCEPT {
if constexpr(is_container_v<Type>) {
static meta_view_node node{
[]() {
if constexpr(is_sequence_container_v<Type>) {
return &internal::meta_node<typename Type::size_type>::resolve;
} else {
return &internal::meta_node<typename Type::key_type>::resolve;
}
},
&internal::meta_node<typename Type::value_type>::resolve,
nullptr, // void(* const next)(meta_handle) ENTT_NOEXCEPT;
nullptr, // void(* const prev)(meta_handle) ENTT_NOEXCEPT;
nullptr, // bool(* const empty)(meta_handle) ENTT_NOEXCEPT;
nullptr, // void(* const clear)(meta_handle);
[](meta_handle instance) -> typename meta_view_node::size_type {
return (*instance).cast<Type>().size();
}, // size_type(* const size)(meta_handle) ENTT_NOEXCEPT;
nullptr, // meta_any(* const begin)(meta_handle) ENTT_NOEXCEPT;
nullptr, // meta_any(* const end)(meta_handle) ENTT_NOEXCEPT;
nullptr, // meta_any(* const erase)(meta_handle, meta_handle);
nullptr, // meta_any(* const insert)(meta_handle, meta_any, meta_any);
nullptr, // bool(* const set)(meta_handle, meta_any, meta_any);
nullptr // meta_any(* const get)(meta_handle, meta_any);
};
return &node;
} else {
return nullptr;
}
}
- add meta_handle::operator-> that returns a meta_any * (easier to use directly)
- use a dedicate class template to specialize meta views for a better support to customizations

View File

@@ -10,6 +10,7 @@
* [Reflection in a nutshell](#reflection-in-a-nutshell)
* [Any as in any type](#any-as-in-any-type)
* [Enjoy the runtime](#enjoy-the-runtime)
* [Container support](#container-support)
* [Policies: the more, the less](#policies-the-more-the-less)
* [Named constants and enums](#named-constants-and-enums)
* [Properties and meta objects](#properties-and-meta-objects)
@@ -28,7 +29,7 @@ allocations. In one word: unsatisfactory.<br/>
I finally decided to write a built-in, non-intrusive and macro-free runtime
reflection system for `EnTT`. Maybe I didn't do better than others or maybe yes,
time will tell me, but at least I can model this tool around the library to
which it belongs and not vice versa.
which it belongs and not the opposite.
# Names and identifiers
@@ -40,7 +41,7 @@ This means that users can assign any type of identifier to the meta objects, as
long as they are numeric. It doesn't matter if they are generated at runtime, at
compile-time or with custom functions.
However, the examples in the following sections are all based on the
That being said, the examples in the following sections are all based on the
`hashed_string` class as provided by this library. Therefore, where an
identifier is required, it's likely that a user defined literal is used as
follows:
@@ -412,6 +413,152 @@ unfortunately beyond the scope of this document.<br/>
I invite anyone interested in the subject to look at the code, experiment and
read the inline documentation to get the best out of this powerful tool.
## Container support
The meta module offers minimal support for containers of all types.<br/>
Here _containers_ doesn't necessarily mean those offered by the C++ standard
library. As long as the user defined containers are subject to the following
rules, they will be automatically intercepted by the reflection system:
* A type is generally considered a container if the `begin`/`end` functions
exist and are resolvable through unqualified lookup.
* A type is considered an associative container if it's a container **and** it
has a public alias declaration called `key_type`.
* A type is considered a key-only associative container if it's an associative
container **and** the public alias declarations `key_type` and `value_type`
refer to the same type.
* A type is considered a sequence container if it's a container and it's not an
associative container.
* A type is considered a dynamic sequence container if it's a sequence container
**and** it offers an `insert` member function that accepts an iterator and a
value to insert.
In addition to the above points, a container must exhibit a set of features that
comply with the standard library guidelines. For example, it must offer a `size`
method and a public alias declaration called `value_type`.
Proxy objects for containers are returned by the `view` member function of the
`meta_any` class:
```cpp
std::vector<int> vec{1, 2, 3};
entt::meta_any any{std::ref(vec)};
entt::meta_container view = any.view();
```
Users aren't expected to _reflect_ containers explicitly. It's sufficient to
assign a container to a `meta_any` object to be able to get its proxy. A proxy
is also contextually convertible to bool to know if it's valid. For example,
invalid proxies are returned when the wrapped object isn't a container.<br/>
To find out if an instance of `meta_any` contains a container or not, simply
query the associated meta type. The latter exposes some methods such as for
example `is_sequence_container` and `is_associative_container` which allow users
to _explore_ the underlying type.
All proxy objects offer the same interface, although the available features
differ from case to case. In particular:
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
```cpp
for(entt::meta_any element: view) {
// ...
}
```
In all cases, given an underlying container of type `C`, the returned element
contains an object of type `C::value_type` which therefore depends on the
actual container.<br/>
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
* The `insert` member function can be used to add elements to the container. It
accepts a couple of erased objects that take on different meaning depending on
the type of container.<br/>
In case of sequence containers, the first argument is a handle to the actual
iterator before which the element will be inserted while the second argument
is the element to insert:
```cpp
auto last = view.end();
// appends an integer to the container
view.insert(last.handle(), 42);
```
As for associative containers instead, the two arguments are respectively the
key and the value to be inserted:
```cpp
view.insert("key"_hs, "value");
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `insert` may silently fail in case of
fixed size containers or whether the arguments aren't at least convertible to
the required types.
* The `erase` member function can be used to remove elements from the container.
It accepts a single argument that takes on different meaning depending on the
type of container.<br/>
In case of sequence containers, the argument is a handle to the actual
iterator to the element to remove:
```cpp
auto first = view.begin();
// removes the first element from the container
view.erase(first);
```
As for associative containers instead, the argument is the key to be removed:
```cpp
view.erase("key"_hs);
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `erase` may silently fail in case of
fixed size containers or whether the argument isn't at least convertible to
the required type.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument that takes on different meaning depending on the type of
container.<br/>
In case of sequence containers, the argument is the position of the element to
return:
```cpp
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
entt::meta_any value = view[pos];
// ...
}
```
As for associative containers instead, the argument is the key of the element
to return:
```cpp
entt::meta_any value = view["key"_hs];
```
In both cases, the function returns an instance of `meta_any` that directly
refers to the actual element. This object may be invalid, for example when
the argument isn't at least convertible to the required type.
Container support is deliberately minimal but theoretically sufficient to
satisfy all needs.
## Policies: the more, the less
Policies are a kind of compile-time directives that can be used when recording