meta: allow assigning properties to opaque meta types

This commit is contained in:
Michele Caini
2019-11-10 17:48:19 +01:00
parent c645cb83a2
commit 82f33b82e3

View File

@@ -236,10 +236,6 @@ meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_
*/
template<typename, typename...>
class extended_meta_factory;
/**
* @brief Meta factory to be used for reflection purposes.
*
@@ -274,6 +270,123 @@ public:
};
/**
* @brief Extended meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
* @tparam Spec Property specialization pack used to disambiguate overloads.
*/
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));
}
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
assign<Step>(std::move(property.first), std::move(property.second));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Property, typename... Other>
std::enable_if_t<!std::is_invocable_v<Property>>
unroll(choice_t<1>, Property &&property, Other &&... other) {
assign<Step>(std::forward<Property>(property));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Func, typename... Other>
void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
}
template<std::size_t>
void unroll(choice_t<0>) {}
template<std::size_t = 0, typename Key, typename... Value>
void assign(Key &&key, Value &&... value) {
static auto property{std::make_tuple(std::forward<Key>(key), std::forward<Value>(value)...)};
static internal::meta_prop_node node{
nullptr,
[]() -> meta_any {
return std::get<0>(property);
},
[]() -> meta_any {
if constexpr(sizeof...(Value) == 0) {
return {};
} else {
return std::get<1>(property);
}
}
};
ENTT_ASSERT(!duplicate(node.key(), *curr));
node.next = *curr;
*curr = &node;
}
public:
/**
* @brief Constructs an extended factory from a given node.
* @param target The underlying node to which to assign the properties.
*/
meta_factory(entt::internal::meta_prop_node **target)
: curr{target}
{}
/**
* @brief Assigns a property to the last meta object created.
*
* Both the key and the value (if any) must be at least copy constructible.
*
* @tparam PropertyOrKey Type of the property or property key.
* @tparam Value Optional type of the property value.
* @param property_or_key Property or property key.
* @param value Optional property value.
* @return A meta factory for the parent type.
*/
template<typename PropertyOrKey, typename... Value>
auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
if constexpr(sizeof...(Value) == 0) {
unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
} else {
assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
}
return meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
}
/**
* @brief Assigns properties to the last meta object created.
*
* Both the keys and the values (if any) must be at least copy
* constructible.
*
* @tparam Property Types of the properties.
* @param property Properties to assign to the last meta object created.
* @return A meta factory for the parent type.
*/
template <typename... Property>
auto props(Property... property) && {
unroll(choice<3>, std::forward<Property>(property)...);
return meta_factory<Type, Spec..., Property...>{curr};
}
private:
entt::internal::meta_prop_node **curr{nullptr};
};
/**
* @brief Basic meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
@@ -301,7 +414,7 @@ class meta_factory<Type> {
node->hook = internal::meta_info<>::global;
*internal::meta_info<>::global = node;
return extended_meta_factory<Type>{&node->prop};
return meta_factory<Type, Type>{&node->prop};
}
public:
@@ -456,7 +569,7 @@ public:
node.next = type->ctor;
type->ctor = &node;
return extended_meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
return meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
}
/**
@@ -489,7 +602,7 @@ public:
node.next = type->ctor;
type->ctor = &node;
return extended_meta_factory<Type, Type(Args...)>{&node.prop};
return meta_factory<Type, Type(Args...)>{&node.prop};
}
/**
@@ -607,7 +720,7 @@ public:
curr->next = type->data;
type->data = curr;
return extended_meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&curr->prop};
return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&curr->prop};
}
/**
@@ -654,7 +767,7 @@ public:
node.next = type->data;
type->data = &node;
return extended_meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
}
/**
@@ -696,7 +809,7 @@ public:
node.next = type->func;
type->func = &node;
return extended_meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
}
/**
@@ -713,138 +826,27 @@ public:
};
/**
* @brief Extended meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
* @tparam Spec Property specialization pack used to disambiguate overloads.
*/
template<typename Type, typename... Spec>
class extended_meta_factory: 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));
}
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
assign<Step>(std::move(property.first), std::move(property.second));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Property, typename... Other>
std::enable_if_t<!std::is_invocable_v<Property>>
unroll(choice_t<1>, Property &&property, Other &&... other) {
assign<Step>(std::forward<Property>(property));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Func, typename... Other>
void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
}
template<std::size_t>
void unroll(choice_t<0>) {}
template<std::size_t = 0, typename Key, typename... Value>
void assign(Key &&key, Value &&... value) {
static auto property{std::make_tuple(std::forward<Key>(key), std::forward<Value>(value)...)};
static internal::meta_prop_node node{
nullptr,
[]() -> meta_any {
return std::get<0>(property);
},
[]() -> meta_any {
if constexpr(sizeof...(Value) == 0) {
return {};
} else {
return std::get<1>(property);
}
}
};
ENTT_ASSERT(!duplicate(node.key(), *curr));
node.next = *curr;
*curr = &node;
}
public:
/**
* @brief Constructs an extended factory from a given node.
* @param target The underlying node to which to assign the properties.
*/
extended_meta_factory(entt::internal::meta_prop_node **target)
: curr{target}
{}
/**
* @brief Assigns a property to the last meta object created.
*
* Both the key and the value (if any) must be at least copy constructible.
*
* @tparam PropertyOrKey Type of the property or property key.
* @tparam Value Optional type of the property value.
* @param property_or_key Property or property key.
* @param value Optional property value.
* @return A meta factory for the parent type.
*/
template<typename PropertyOrKey, typename... Value>
auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
if constexpr(sizeof...(Value) == 0) {
unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
} else {
assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
}
return extended_meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
}
/**
* @brief Assigns properties to the last meta object created.
*
* Both the keys and the values (if any) must be at least copy
* constructible.
*
* @tparam Property Types of the properties.
* @param property Properties to assign to the last meta object created.
* @return A meta factory for the parent type.
*/
template <typename... Property>
auto props(Property... property) && {
unroll(choice<3>, std::forward<Property>(property)...);
return extended_meta_factory<Type, Spec..., Property...>{curr};
}
private:
entt::internal::meta_prop_node **curr{nullptr};
};
/**
* @brief Utility function to use for reflection.
*
* This is the point from which everything starts.<br/>
* By invoking this function with a type that is not yet reflected, a meta type
* is created to which it will be possible to attach meta objects through a
* dedicated factory. If no type is provided, a generic meta factory is returned
* instead.
* dedicated factory. If no type is provided instead, a generic meta factory is
* returned.
*
* @tparam Type Type to reflect, if any.
* @return An eventually generic meta factory.
*/
template<typename... Type>
inline meta_factory<Type...> meta() ENTT_NOEXCEPT {
return meta_factory<Type...>{};
if constexpr(sizeof...(Type) == 0) {
return meta_factory{};
} else {
auto * const node = internal::meta_info<Type...>::resolve();
// extended meta factory to allow assigning properties to opaque meta types
return meta_factory<Type..., Type...>{&node->prop};
}
}