test: meta and plugins

This commit is contained in:
Michele Caini
2019-12-09 14:56:19 +01:00
parent 169dcbcd74
commit 84fb3694f2
7 changed files with 204 additions and 41 deletions

5
TODO
View File

@@ -29,10 +29,9 @@
- generators as arguments to family::type overloads
- generators to constructors
- bind identifiers by name (as for meta)
* meta
- shared context
- bind<T>("name"_hs)
- reintroduce old-fashion family and add a new family-like thing with generators
- families should be defined as out-of-class to guarantee the same identifiers for the same types
- update doc: family, dispatcher, emitter, registry, meta, across boundaries
- update tests
- review and suppress warnings, if any
- remove ENTT_API from OPAQUE_TYPE

View File

@@ -256,8 +256,8 @@ class meta_factory;
*/
template<typename Type, typename... Spec>
class meta_factory<Type, Spec...>: public meta_factory<Type> {
bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
return node && (node->key() == key || duplicate(key, node->next));
bool exists(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
return node && (node->key() == key || exists(key, node->next));
}
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
@@ -309,7 +309,7 @@ class meta_factory<Type, Spec...>: public meta_factory<Type> {
}
};
ENTT_ASSERT(!duplicate(node.key(), *curr));
ENTT_ASSERT(!exists(node.key(), *curr));
node.next = *curr;
*curr = &node;
}
@@ -373,13 +373,13 @@ private:
template<typename Type>
class meta_factory<Type> {
template<typename Node>
bool duplicate(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
return node && (node == candidate || duplicate(candidate, node->next));
bool exists(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
return node && (node == candidate || exists(candidate, node->next));
}
template<typename Node>
bool duplicate(const ENTT_ID_TYPE identifier, const Node *node) ENTT_NOEXCEPT {
return node && (node->identifier == identifier || duplicate(identifier, node->next));
bool exists(const ENTT_ID_TYPE identifier, const Node *node) ENTT_NOEXCEPT {
return node && (node->identifier == identifier || exists(identifier, node->next));
}
public:
@@ -391,11 +391,11 @@ public:
auto type(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
auto * const node = internal::meta_info<Type>::resolve();
ENTT_ASSERT(!duplicate(identifier, internal::meta_info<>::node));
ENTT_ASSERT(!duplicate(node, internal::meta_info<>::node));
ENTT_ASSERT(!exists(identifier, *internal::meta_info<>::context()));
ENTT_ASSERT(!exists(node, *internal::meta_info<>::context()));
node->identifier = identifier;
node->next = internal::meta_info<>::node;
internal::meta_info<>::node = node;
node->next = *internal::meta_info<>::context();
*internal::meta_info<>::context() = node;
return meta_factory<Type, Type>{&node->prop};
}
@@ -422,7 +422,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->base));
ENTT_ASSERT(!exists(&node, type->base));
node.next = type->base;
type->base = &node;
@@ -452,7 +452,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->conv));
ENTT_ASSERT(!exists(&node, type->conv));
node.next = type->conv;
type->conv = &node;
@@ -485,7 +485,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->conv));
ENTT_ASSERT(!exists(&node, type->conv));
node.next = type->conv;
type->conv = &node;
@@ -522,7 +522,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->ctor));
ENTT_ASSERT(!exists(&node, type->ctor));
node.next = type->ctor;
type->ctor = &node;
@@ -555,7 +555,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->ctor));
ENTT_ASSERT(!exists(&node, type->ctor));
node.next = type->ctor;
type->ctor = &node;
@@ -667,8 +667,8 @@ public:
curr = &node;
}
ENTT_ASSERT(!duplicate(identifier, type->data));
ENTT_ASSERT(!duplicate(curr, type->data));
ENTT_ASSERT(!exists(identifier, type->data));
ENTT_ASSERT(!exists(curr, type->data));
curr->identifier = identifier;
curr->next = type->data;
type->data = curr;
@@ -714,8 +714,8 @@ public:
&internal::getter<Type, Getter, Policy>
};
ENTT_ASSERT(!duplicate(identifier, type->data));
ENTT_ASSERT(!duplicate(&node, type->data));
ENTT_ASSERT(!exists(identifier, type->data));
ENTT_ASSERT(!exists(&node, type->data));
node.identifier = identifier;
node.next = type->data;
type->data = &node;
@@ -756,8 +756,8 @@ public:
}
};
ENTT_ASSERT(!duplicate(identifier, type->func));
ENTT_ASSERT(!duplicate(&node, type->func));
ENTT_ASSERT(!exists(identifier, type->func));
ENTT_ASSERT(!exists(&node, type->func));
node.identifier = identifier;
node.next = type->func;
type->func = &node;
@@ -772,10 +772,12 @@ public:
* functions and properties, as well as its constructors, destructors and
* conversion functions if any.<br/>
* Base classes aren't reset but the link between the two types is removed.
*
* @return An extended meta factory for the given type.
*/
void reset() ENTT_NOEXCEPT {
auto reset() ENTT_NOEXCEPT {
auto * const node = internal::meta_info<Type>::resolve();
auto **it = &internal::meta_node<>::node;
auto **it = internal::meta_info<>::context();
while(*it && *it != node) {
it = &(*it)->next;
@@ -806,6 +808,26 @@ public:
node->identifier = {};
node->next = nullptr;
node->dtor = nullptr;
return meta_factory<Type, Type>{&node->prop};
}
/**
* @brief Imports a meta type from another context.
* @param identifier Unique identifier.
* @return An extended meta factory for the given type.
*/
auto import(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
auto * const ctx = *internal::meta_info<>::context();
ENTT_ASSERT(exists(identifier, ctx));
ENTT_ASSERT(!exists(internal::meta_info<Type>::resolve(), ctx));
internal::meta_info<Type>::alias = internal::find_if([identifier](const auto *curr) {
return curr->identifier == identifier;
}, ctx);
return meta_factory<Type, Type>{&internal::meta_info<Type>::resolve()->prop};
}
};
@@ -848,7 +870,7 @@ inline meta_type resolve() ENTT_NOEXCEPT {
inline meta_type resolve(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
return internal::find_if([identifier](const auto *curr) {
return curr->identifier == identifier;
}, internal::meta_info<>::node);
}, *internal::meta_info<>::context());
}
@@ -860,7 +882,7 @@ inline meta_type resolve(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
template<typename Op>
inline std::enable_if_t<std::is_invocable_v<Op, meta_type>, void>
resolve(Op op) {
internal::visit<meta_type>(std::move(op), internal::meta_info<>::node);
internal::visit<meta_type>(std::move(op), *internal::meta_info<>::context());
}

View File

@@ -192,8 +192,9 @@ bool compare(const void *lhs, const void *rhs) {
template<typename... Type>
struct ENTT_API meta_node {
static_assert(std::is_same_v<Type..., std::remove_cv_t<std::remove_reference_t<Type>>...>);
inline static meta_type_node *alias = nullptr;
static meta_type_node * resolve() ENTT_NOEXCEPT {
inline static meta_type_node * resolve() ENTT_NOEXCEPT {
static meta_type_node node{
{},
nullptr,
@@ -211,22 +212,23 @@ struct ENTT_API meta_node {
std::is_member_function_pointer_v<Type...>,
std::extent_v<Type...>,
&compare<Type...>, // workaround for an issue with VS2017
[]() ENTT_NOEXCEPT -> meta_type_node * {
return meta_node<std::remove_const_t<std::remove_pointer_t<Type>>...>::resolve();
},
[]() ENTT_NOEXCEPT -> meta_type_node * {
return meta_node<std::remove_const_t<std::remove_extent_t<Type>>...>::resolve();
}
&meta_node<std::remove_const_t<std::remove_pointer_t<Type>>...>::resolve,
&meta_node<std::remove_const_t<std::remove_extent_t<Type>>...>::resolve
};
return &node;
return alias ? alias : &node;
}
};
template<>
struct ENTT_API meta_node<> {
inline static meta_type_node *node = nullptr;
inline static meta_type_node **shared = nullptr;
inline static meta_type_node ** context() ENTT_NOEXCEPT {
static meta_type_node *local = nullptr;
return shared ? shared : &local;
}
};
@@ -243,6 +245,22 @@ struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>...>
*/
/*! @brief Opaque container for a meta context. */
struct meta_ctx {
/**
* @brief Binds the meta system to the given context.
* @param other A valid context to which to bind.
*/
static void bind(meta_ctx other) ENTT_NOEXCEPT {
ENTT_ASSERT(!internal::meta_info<>::shared);
internal::meta_info<>::shared = other.ctx;
}
private:
internal::meta_type_node **ctx{internal::meta_info<>::context()};
};
/**
* @brief Opaque container for values of any type.
*

View File

@@ -1917,7 +1917,7 @@ TEST_F(Meta, PropertiesAndCornerCases) {
}
TEST_F(Meta, Reset) {
ASSERT_NE(entt::internal::meta_info<>::node, nullptr);
ASSERT_NE(*entt::internal::meta_info<>::context(), nullptr);
entt::meta<char>().reset();
entt::meta<concrete_type>().reset();
@@ -1935,7 +1935,7 @@ TEST_F(Meta, Reset) {
entt::meta<another_abstract_type>().reset();
entt::meta<unsigned int>().reset();
ASSERT_EQ(entt::internal::meta_info<>::node, nullptr);
ASSERT_EQ(*entt::internal::meta_info<>::context(), nullptr);
ASSERT_FALSE(entt::resolve("char"_hs));
ASSERT_FALSE(entt::resolve("base"_hs));
@@ -1949,7 +1949,7 @@ TEST_F(Meta, Reset) {
ASSERT_FALSE(entt::resolve("another_abstract_type"_hs));
ASSERT_FALSE(entt::resolve("concrete"_hs));
ASSERT_EQ(entt::internal::meta_info<>::node, nullptr);
ASSERT_EQ(*entt::internal::meta_info<>::context(), nullptr);
Meta::SetUpAfterUnregistration();
entt::meta_any any{42.};

View File

@@ -0,0 +1,50 @@
#define CR_HOST
#include <cr.h>
#include <gtest/gtest.h>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include "types.h"
TEST(Lib, Meta) {
entt::meta<double>().type("double"_hs);
entt::meta<int>().type("int"_hs);
ASSERT_FALSE(entt::resolve("position"_hs));
ASSERT_FALSE(entt::resolve<double>().conv<int>());
userdata ud{};
cr_plugin ctx;
ctx.userdata = &ud;
cr_plugin_load(ctx, PLUGIN);
cr_plugin_update(ctx);
ASSERT_TRUE(entt::resolve("position"_hs));
ASSERT_TRUE(entt::resolve<double>().conv<int>());
auto pos = entt::resolve("position"_hs).construct(42., 3.);
auto vel = entt::resolve("velocity"_hs).ctor().invoke();
ASSERT_TRUE(pos && vel);
ASSERT_EQ(pos.type().data("x"_hs).type(), entt::resolve<int>());
ASSERT_TRUE(pos.type().data("y"_hs).get(*pos).try_cast<int>());
ASSERT_EQ(pos.type().data("x"_hs).get(*pos).cast<int>(), 42);
ASSERT_EQ(pos.type().data("y"_hs).get(*pos).cast<int>(), 3);
ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
ASSERT_TRUE(vel.type().data("dy"_hs).get(*vel).convert<int>());
ASSERT_EQ(vel.type().data("dx"_hs).get(*vel).cast<double>(), 0.);
ASSERT_EQ(vel.type().data("dy"_hs).get(*vel).cast<double>(), 0.);
ASSERT_EQ(ud.any.type(), entt::resolve<int>());
ASSERT_EQ(ud.any.cast<int>(), 42);
// these objects have been initialized from a different context
pos.emplace<void>();
vel.emplace<void>();
ud.any.emplace<void>();
cr_plugin_close(ctx);
}

View File

@@ -0,0 +1,53 @@
#include <cr.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include "types.h"
position create_position(int x, int y) {
return position{x, y};
}
void set_up() {
entt::meta<double>().import("double"_hs);
entt::meta<int>().import("int"_hs);
entt::meta<double>().conv<int>();
entt::meta<position>()
.type("position"_hs)
.ctor<&create_position>()
.data<&position::x>("x"_hs)
.data<&position::y>("y"_hs);
entt::meta<velocity>()
.type("velocity"_hs)
.ctor<>()
.data<&velocity::dx>("dx"_hs)
.data<&velocity::dy>("dy"_hs);
}
void tear_down() {
entt::meta<position>().reset();
entt::meta<velocity>().reset();
}
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
switch (operation) {
case CR_LOAD:
entt::meta_ctx::bind(static_cast<userdata *>(ctx->userdata)->ctx);
set_up();
break;
case CR_STEP:
static_cast<userdata *>(ctx->userdata)->any = 42;
break;
case CR_UNLOAD:
tear_down();
break;
case CR_CLOSE:
// nothing to do here, this is only a test.
break;
}
return 0;
}

View File

@@ -0,0 +1,21 @@
#ifndef ENTT_PLUGIN_META_TYPES_H
#define ENTT_PLUGIN_META_TYPES_H
#include <entt/meta/meta.hpp>
struct position {
int x{};
int y{};
};
struct velocity {
double dx{};
double dy{};
};
struct userdata {
entt::meta_ctx ctx;
entt::meta_any any;
};
#endif // ENTT_PLUGIN_META_TYPES_H