-
-
Notifications
You must be signed in to change notification settings - Fork 897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Component polymorphism support #871
base: wip
Are you sure you want to change the base?
Changes from all commits
75ca22f
d668113
f4741c9
24b6306
3f3749e
f943b91
2c39cfc
641478b
07751cc
d08c303
7fd0ad7
df4271a
b77a527
17d39b7
ca6f502
379722b
7aed523
cac84c7
cb811b0
1d15cb6
bf76404
1abb428
da0d8e3
56dfa4d
bfe4668
e795daf
6255e8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#ifndef ENTT_ENTITY_POLY_STORAGE_MIXIN_HPP | ||
#define ENTT_ENTITY_POLY_STORAGE_MIXIN_HPP | ||
|
||
#include "storage.hpp" | ||
#include "poly_type_traits.hpp" | ||
#include "sigh_storage_mixin.hpp" | ||
|
||
|
||
namespace entt { | ||
|
||
template<typename Entity, typename Type, typename Allocator> | ||
class poly_type; | ||
|
||
/** | ||
* @brief Storage mixin for polymorphic component types | ||
* @tparam Storage underlying storage type | ||
* @tparam Entity entity type | ||
* @tparam Type value type | ||
*/ | ||
template<typename Storage, typename = void> | ||
struct poly_storage_mixin : Storage { | ||
/*! @brief Underlying value type. */ | ||
using value_type = typename Storage::value_type; | ||
/*! @brief Underlying entity identifier. */ | ||
using entity_type = typename Storage::entity_type; | ||
|
||
/*! @brief Inherited constructors. */ | ||
using Storage::Storage; | ||
|
||
/** | ||
* @brief Forwards variables to mixins, if any. | ||
* @param value A variable wrapped in an opaque container. | ||
*/ | ||
void bind(any value) noexcept override { | ||
if(auto *reg = any_cast<basic_registry<entity_type>>(&value); reg) { | ||
bind_all_parent_types(*reg, poly_parent_types_t<value_type>{}); | ||
} | ||
Storage::bind(std::move(value)); | ||
} | ||
|
||
private: | ||
template<typename... ParentTypes> | ||
void bind_all_parent_types(basic_registry<entity_type>& reg, [[maybe_unused]] type_list<ParentTypes...>) { | ||
(reg.ctx().template emplace<poly_type<entity_type, ParentTypes, poly_type_allocator_t<ParentTypes>>>().bind_storage(this), ...); | ||
reg.ctx().template emplace<poly_type<entity_type, value_type, poly_type_allocator_t<value_type>>>().bind_storage(this); | ||
} | ||
}; | ||
|
||
|
||
} // entt | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
#ifndef ENTT_ENTITY_POLY_TYPE_TRAITS_HPP | ||
#define ENTT_ENTITY_POLY_TYPE_TRAITS_HPP | ||
|
||
#include "../core/type_info.hpp" | ||
#include "../core/type_traits.hpp" | ||
|
||
|
||
namespace entt { | ||
|
||
/** @brief Validates a polymorphic type and returns it unchanged */ | ||
template<typename T, typename... Parents> | ||
struct poly_type_validate; | ||
|
||
/** | ||
* @brief Declares direct parent types of polymorphic component type. | ||
* By default it uses the list from T::direct_parent_types, if it present, otherwise empty list is used. | ||
* All parent types must be declared polymorphic. | ||
* @code{.cpp} | ||
* struct A {}; | ||
* struct B : A {}; | ||
* | ||
* struct entt::poly_direct_parent_types<A> { | ||
* using parent_types = entt::type_list<>; // declares A as polymorphic type with no parents | ||
* } | ||
* struct entt::poly_direct_parent_types<B> { | ||
* using parent_types = entt::type_list<A>; // declares B as polymorphic type with parent A | ||
* } | ||
* @endcode | ||
* @tparam T polymorphic component type | ||
*/ | ||
template<typename T, typename = void> | ||
struct poly_direct_parent_types { | ||
/** @brief entt::type_list of direct parent types */ | ||
using parent_types = type_list<>; | ||
|
||
/** @brief used to detect, if this template was specialized for a type */ | ||
using not_redefined_tag = void; | ||
}; | ||
|
||
/** @copydoc poly_direct_parent_types */ | ||
template<typename T> | ||
struct poly_direct_parent_types<T, std::void_t<typename T::direct_parent_types>> { | ||
using parent_types = typename T::direct_parent_types; | ||
}; | ||
|
||
/** | ||
* @brief For given polymorphic component type returns entt::type_list of direct parent types, | ||
* for non polymorphic type will return empty list | ||
* @tparam T type to get parents from | ||
*/ | ||
template<typename T> | ||
using poly_direct_parent_types_t = typename poly_direct_parent_types<T>::parent_types; | ||
|
||
/** | ||
* @brief Declares list of all parent types of polymorphic component type. | ||
* By default will concatenates list of all direct parent types and all lists of all parents for each parent type. All parent | ||
* types must be declared polymorphic. | ||
* @tparam T | ||
*/ | ||
template<typename T, typename = void> | ||
struct poly_parent_types { | ||
private: | ||
template<typename, typename...> | ||
struct all_parent_types; | ||
|
||
template<typename... DirectParents> | ||
struct all_parent_types<type_list<DirectParents...>> { | ||
using types = type_list_cat_t<type_list<DirectParents...>, typename poly_parent_types<DirectParents>::parent_types...>; | ||
}; | ||
|
||
public: | ||
/** @brief entt::type_list of all parent types */ | ||
using parent_types = typename all_parent_types<poly_direct_parent_types_t<T>>::types; | ||
|
||
/** @brief used to detect, if this template was specialized for a type */ | ||
using not_redefined_tag = void; | ||
}; | ||
|
||
/** @copydoc poly_parent_types */ | ||
template<typename T> | ||
struct poly_parent_types<T, std::void_t<typename T::all_parent_types>> { | ||
using parent_types = typename T::all_parent_types; | ||
}; | ||
|
||
/** | ||
* @brief For given polymorphic component type returns entt::type_list of all parent types, | ||
* for non polymorphic type will return empty list | ||
* @tparam T type to get parents from | ||
*/ | ||
template<typename T> | ||
using poly_parent_types_t = typename poly_parent_types<T>::parent_types; | ||
|
||
/** | ||
* Used to declare allocator type for polymorphic component type | ||
* @tparam Type | ||
*/ | ||
template<typename Type, typename = void> | ||
struct poly_type_allocator { | ||
/** @brief allocator type */ | ||
using type = std::allocator<Type>; | ||
}; | ||
|
||
/** @copydoc poly_type_allocator */ | ||
template<typename Type> | ||
using poly_type_allocator_t = typename poly_type_allocator<Type>::type; | ||
|
||
/** | ||
* @brief For a given type, detects, if it was declared polymorphic. Type considered polymorphic, if it was either:<br/> | ||
* - inherited from entt::inherit<br/> | ||
* - declared types direct_parent_types or all_parent_types<br/> | ||
* - for this type there is specialization of either entt::poly_direct_parent_types or entt::poly_parent_types<br/> | ||
* @tparam T type to check | ||
*/ | ||
template<typename T, typename = void> | ||
struct is_poly_type { | ||
static constexpr bool value = true; | ||
}; | ||
|
||
/** @copydoc is_poly_type */ | ||
template<typename T> | ||
struct is_poly_type<T, std::void_t<typename poly_direct_parent_types<T>::not_redefined_tag, typename poly_parent_types<T>::not_redefined_tag>> { | ||
static constexpr bool value = false; | ||
}; | ||
|
||
/** @copydoc is_poly_type */ | ||
template<typename T> | ||
inline constexpr bool is_poly_type_v = is_poly_type<T>::value; | ||
|
||
/** @copydoc poly_type_validate */ | ||
template<typename T, typename... Parents> | ||
struct poly_type_validate<T, type_list<Parents...>> { | ||
static_assert(std::is_same_v<std::decay_t<T>, T>, "only decayed types allowed to be declared as polymorphic"); | ||
static_assert(is_poly_type_v<T>, "validating non-polymorphic type (probably some polymorphic type inherits type, that was not declared polymorphic)"); | ||
static_assert(std::bool_constant<(is_poly_type_v<Parents> && ...)>::value, "all parent types of a polymorphic type must be also polymorphic"); | ||
static_assert(std::bool_constant<(!std::is_pointer_v<std::remove_pointer_t<T>> && ... && !std::is_pointer_v<std::remove_pointer_t<Parents>>)>::value, "double pointers are not allowed as a polymorphic components"); | ||
static_assert(std::bool_constant<((std::is_pointer_v<T> == std::is_pointer_v<Parents>) && ...)>::value, "you cannot mix pointer-based and value-based polymorphic components inside one hierarchy"); | ||
|
||
/** @brief same input type, but validated */ | ||
using type = T; | ||
}; | ||
|
||
/** @copydoc poly_type_validate */ | ||
template<typename T> | ||
using poly_type_validate_t = typename poly_type_validate<T, poly_parent_types_t<T>>::type; | ||
|
||
/** | ||
* @brief Returns if one polymorphic component type is same, or is declared as a parent of another polymorphic type | ||
* @tparam Parent Parent type | ||
* @tparam Type Child Type | ||
*/ | ||
template<typename Parent, typename Type> | ||
inline constexpr bool is_poly_parent_of_v = is_poly_type_v<Type> && (type_list_contains_v<poly_parent_types_t<Type>, Parent> || std::is_same_v<Type, Parent>); | ||
|
||
/** | ||
* @brief Used to inherit from all given parent types and declare inheriting type polymorphic with given direct parents. | ||
* All parent types are required to be polymorphic | ||
* @code{.cpp} | ||
* struct A : public entt::inherit<> {}; // base polymorphic type with no parents | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the end of the day, why I as an user should decide to inherit from a third party library class (and thus pollute my types and hierarchies) if you also give me an alternative? That is, is struct derived: base { /* definition */ }; This is my type, part of my application, it tells me about my intention and purpose. Why one should turn it into something like: struct derived: entt::inherit<base> { /* definition */ }; I don't really see any reason for which I would do that as a final user. Granted, I'm a huge fan of non-invasive designe, solutions, whatever but still, I can accept an invasive approach if there is a reason and I don't understand the benefit here. Therefore I'm a bit puzzled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* struct B : public entt::inherit<A> {}; // B inherits A, and declared as polymorphic component with direct parents {A} | ||
* struct C : public entt::inherit<B> {}; // C inherits B, and now has direct parents {B} and all parents {A, B} | ||
* @endcode | ||
* @tparam Parents list of parent types | ||
*/ | ||
template<typename... Parents> | ||
struct inherit : public poly_type_validate_t<Parents>... { | ||
using direct_parent_types = type_list<Parents...>; | ||
}; | ||
|
||
} // entt | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is contrived imho. You expect a user to want to bind all parent types but, in fact, trying to automate this as much as possible takes away the user's ability to choose.
I'm not sure this is the right way to go. The whole library is also designed on the opposite principle.
Without changing anything yet, can you explain what the burden is on the user in the other case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User can directly declare, what types are parent to the current component by specializing
entt::direct_parent_types
orentt::parent_types
. These two provide complete freedom in declaring parent type list for each separate type.So having control over this particular method only provides the ability to bind parent list, different from the one, that is already declared. However, the declared one is currently used only in this method and a bunch of static asserts, and I'm not sure, that modifying list in this method could be useful.