Skip to content
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

Open
wants to merge 27 commits into
base: wip
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
75ca22f
started working on cleaner implementation
zheka2304 Apr 3, 2022
d668113
cleanup & iterator implementation for single entity
Apr 5, 2022
f4741c9
each_poly method
Apr 5, 2022
24b6306
comment adjustments
Apr 5, 2022
3f3749e
poly_type_base -> poly_types_data
Apr 5, 2022
f943b91
correctly handling const qualifier for polymorphic components
zheka2304 Apr 5, 2022
2c39cfc
more correct const handling
zheka2304 Apr 5, 2022
641478b
registry.poly_remove method
zheka2304 Apr 9, 2022
07751cc
sigh support for poly types
zheka2304 Apr 10, 2022
d08c303
added new header files to cmake
zheka2304 Apr 10, 2022
7fd0ad7
fixed includes
zheka2304 Apr 10, 2022
df4271a
replaced poly_types_data with poly_types_accessor, moved poly algorit…
Apr 14, 2022
b77a527
separated poly algorithms from registry type, some cleanup and polishing
Apr 15, 2022
17d39b7
cleaned up poly_storage_mixin, using default poly_sigh_mixin for sign…
Apr 15, 2022
ca6f502
tests, cleanup and poly_count function
zheka2304 Apr 17, 2022
379722b
optimized poly_count
Apr 19, 2022
7aed523
fixed MSVC v141 build
Apr 20, 2022
cac84c7
code coverage trick for no-iterations tests
Apr 20, 2022
cb811b0
* replaced converting_iterator + poly_type.pools() with poly_type.eac…
Apr 25, 2022
1d15cb6
Made polymorphic algorithms allocator aware
zheka2304 Apr 30, 2022
bf76404
using simple inlined vector for bound pools to avoid heap allocation …
zheka2304 May 1, 2022
1abb428
virtual destructor for test poly types for clang
zheka2304 May 1, 2022
da0d8e3
fixed extracting pointer type from allocator
zheka2304 May 1, 2022
56dfa4d
fixed msvc (I hope so)
zheka2304 May 2, 2022
bfe4668
possible fix for codecov issue
May 11, 2022
e795daf
fixes after rebase
zheka2304 May 23, 2022
6255e8b
handling empty polymorphic types correctly
zheka2304 May 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/poly_storage_mixin.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/poly_type_traits.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/polymorphic.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sigh_storage_mixin.hpp>
Expand Down
52 changes: 52 additions & 0 deletions src/entt/entity/poly_storage_mixin.hpp
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...>) {
Copy link
Owner

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?

Copy link
Author

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 or entt::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.

(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
171 changes: 171 additions & 0 deletions src/entt/entity/poly_type_traits.hpp
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
Copy link
Owner

Choose a reason for hiding this comment

The 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 inherit really required or just syntactic sugar?
I can't yet wrap my mind around of it but it looks like one can just make it work without inheriting from it and this would make the whole thing more natural in a sense:

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entt::inherit is just a shorter alternative for using direct_parent_types = entt::type_list<Parent1, Parent2, ...> or separate declaration with entt::direct_parent_types, so yes, it is just syntactic sugar. But for me personally, it just looks a lot nicer, so I would like to keep it just as one of several ways of declaring hierarchy.

* 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
Loading