poly: first implementation
This commit is contained in:
@@ -87,10 +87,11 @@ Here is a brief, yet incomplete list of what it offers today:
|
||||
* Views and groups to iterate entities and components and allow different access
|
||||
patterns, from **perfect SoA** to fully random.
|
||||
* A lot of **facilities** built on top of the entity-component system to help
|
||||
the users and avoid reinventing the wheel (dependencies, snapshot, actor
|
||||
class, support for **reactive systems** and so on).
|
||||
the users and avoid reinventing the wheel (dependencies, snapshot, handles,
|
||||
support for **reactive systems** and so on).
|
||||
* The smallest and most basic implementation of a **service locator** ever seen.
|
||||
* A built-in, non-intrusive and macro-free runtime **reflection system**.
|
||||
* **Static polymorphism** made simple and within everyone's reach.
|
||||
* A **cooperative scheduler** for processes of any type.
|
||||
* All that is needed for **resource management** (cache, loaders, handles).
|
||||
* Delegates, **signal handlers** (with built-in support for collectors) and a
|
||||
|
||||
161
docs/md/poly.md
Normal file
161
docs/md/poly.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Crash Course: poly
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Other libraries](#other-libraries)
|
||||
* [Concept and implementation](#concept-and-implementation)
|
||||
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome
|
||||
to obtain.<br/>
|
||||
This module aims to make it simple and easy to use.
|
||||
|
||||
The library allows to define _concepts_ as interfaces to fullfill with concrete
|
||||
classes withouth having to inherit from a common base.<br/>
|
||||
This is, among others, one of the advantages of static polymorphism in general
|
||||
and of a generic wrapper like that offered by the `poly` class template in
|
||||
particular.<br/>
|
||||
What users get is an object that can be passed around as such and not through a
|
||||
reference or a pointer, as happens when it comes to working with dynamic
|
||||
polymorphism.
|
||||
|
||||
Since the `poly` class template makes use of `entt::any` internally, it supports
|
||||
most of its features. Among the most important, the possibility to create
|
||||
aliases to existing objects and therefore not managed directly. This allows
|
||||
users to exploit the static polymorphism while maintaining ownership of their
|
||||
objects.<br/>
|
||||
Likewise, the `poly` class template also benefits from the small buffer
|
||||
optimization offered by the `entt::any` class and therefore minimizes the number
|
||||
of allocations, avoiding them altogether where possible.
|
||||
|
||||
## Other libraries
|
||||
|
||||
There are some very interesting libraries regarding static polymorphism.<br/>
|
||||
Among all, the two that I prefer are:
|
||||
|
||||
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
||||
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
||||
a class template that makes it easy to define a type-erasing polymorphic
|
||||
object wrapper.
|
||||
|
||||
The former is admittedly an experimental library, with many interesting ideas.
|
||||
I've some doubts about the usefulness of some features in real world projects,
|
||||
but perhaps my ignorance comes into play here. In my opinion, its only flaw is
|
||||
the API which I find slightly more cumbersome than other solutions.<br/>
|
||||
The latter was undoubtedly a source of inspiration for this module, although I
|
||||
opted for different choices in the implementation of both the final API and some
|
||||
features.
|
||||
|
||||
Either way, the authors are gurus of the C++ community, people I only have to
|
||||
learn from.
|
||||
|
||||
# Concept and implementation
|
||||
|
||||
The first thing to do to create a _type-erasing polymorphic object wrapper_ (to
|
||||
use the terminology introduced by Eric Niebler) is to define a _concept_ that
|
||||
types will have to adhere to.<br/>
|
||||
In `EnTT`, this translates into the definition of a template class as follows:
|
||||
|
||||
```cpp
|
||||
template<typename Base>
|
||||
struct Drawable: Base {
|
||||
void draw() { this->template invoke<0>(*this); }
|
||||
};
|
||||
```
|
||||
|
||||
The example is purposely minimal but the functions can receive values and return
|
||||
arguments. The former will be returned by the call to `invoke`, the latter must
|
||||
be passed to the same function after the reference to `this` instead.<br/>
|
||||
As for `invoke`, this is a name that is injected into the _concept_ through
|
||||
`Base`, from which one must necessarily inherit. Since it's also a dependent
|
||||
name, the `this-> template` form is unfortunately necessary due to the rules of
|
||||
the language. However, there exists also an alternative that goes through an
|
||||
external call:
|
||||
|
||||
```cpp
|
||||
template<typename Base>
|
||||
struct Drawable: Base {
|
||||
void draw() { entt::poly_call<0>(*this); }
|
||||
};
|
||||
```
|
||||
|
||||
Once the _concept_ is defined, users need to specialize a template variable to
|
||||
tell the system how any type can satisfy its requirements:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
inline constexpr auto entt::poly_impl<Drawable, Type> = entt::value_list<&Type::draw>{};
|
||||
```
|
||||
|
||||
In this case, it's stated that the `draw` method of a generic type will be
|
||||
enough to satisfy the requirements of the `Drawable` concept.<br/>
|
||||
The `poly_impl` variable template can be specialized in a generic way as in the
|
||||
example above, or for a specific type where this satisfies the requirements
|
||||
differently. Moreover, it's easy to specialize it for families of types:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
inline constexpr auto entt::poly_impl<Drawable, std::vector<Type>> = entt::value_list<&std::vector<Type>::size>{};
|
||||
```
|
||||
|
||||
Finally, an implementation doesn't have to consist of just member functions.
|
||||
Free functions are an alternative to fill any gaps in the interface of a type:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
void print(Type &self) { self.print(); }
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr auto entt::poly_impl<Drawable, Type> = entt::value_list<&print<Type>>{};
|
||||
```
|
||||
|
||||
Refer to the variable template definition for more details.
|
||||
|
||||
# Static polymorphism in the wild
|
||||
|
||||
Once the _concept_ and implementation have been introduced, it will be possible
|
||||
to use the `poly` class template to contain instances that meet the
|
||||
requirements:
|
||||
|
||||
```cpp
|
||||
using drawable = entt::poly<Drawable>;
|
||||
|
||||
struct circle {
|
||||
void draw() { /* ... */ }
|
||||
};
|
||||
|
||||
struct square {
|
||||
void draw() { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
drawable d{circle{}};
|
||||
d.draw();
|
||||
|
||||
d = square{};
|
||||
d.draw();
|
||||
```
|
||||
|
||||
The `poly` class template offers a wide range of constructors, from the default
|
||||
one (which will return an uninitialized `poly` object) to the copy and move
|
||||
constructor, as well as the ability to create objects in-place.<br/>
|
||||
Among others, there is a constructor that allows users to wrap unmanaged objects
|
||||
in a `poly` instance:
|
||||
|
||||
```cpp
|
||||
circle c;
|
||||
drawable d{std::ref(c)};
|
||||
```
|
||||
|
||||
In this case, although the interface of the `poly` object doesn't change, it
|
||||
won't construct any element or take care of destroying the referenced object.
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "meta/resolve.hpp"
|
||||
#include "meta/type_traits.hpp"
|
||||
#include "platform/android-ndk-r17.hpp"
|
||||
#include "poly/poly.hpp"
|
||||
#include "process/process.hpp"
|
||||
#include "process/scheduler.hpp"
|
||||
#include "resource/cache.hpp"
|
||||
|
||||
329
src/entt/poly/poly.hpp
Normal file
329
src/entt/poly/poly.hpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#ifndef ENTT_POLY_POLY_HPP
|
||||
#define ENTT_POLY_POLY_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Inline variable designed to contain the definition of a concept.
|
||||
* @tparam Concept A concept class template.
|
||||
* @tparam Type The type for which the definition is provided.
|
||||
*/
|
||||
template<template<typename> class Concept, typename Type>
|
||||
inline constexpr auto poly_impl = value_list{};
|
||||
|
||||
|
||||
/*! @brief Static virtual table factory. */
|
||||
class poly_vtable {
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(*)(Type &, Args...)) {
|
||||
return +[](any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<Type &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(*)(const Type &, Args...)) {
|
||||
return +[](const any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<std::add_const_t<Type> &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(Type:: *)(Args...)) {
|
||||
return +[](any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<Type &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(Type:: *)(Args...) const) {
|
||||
return +[](const any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<std::add_const_t<Type> &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto... Impl>
|
||||
[[nodiscard]] static auto * instance(value_list<Impl...>) {
|
||||
static const auto vtable = std::make_tuple(vtable_entry<Type, Impl>(Impl)...);
|
||||
return &vtable;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a static virtual table for a specific concept and type.
|
||||
* @tparam Concept A concept class template.
|
||||
* @tparam Type The type for which to generate the virtual table.
|
||||
* @return A static virtual table for the given concept and type.
|
||||
*/
|
||||
template<template<typename> class Concept, typename Type>
|
||||
[[nodiscard]] static auto * instance() {
|
||||
return instance<Type>(poly_impl<Concept, Type>);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Inspector class used to infer the type of the virtual table. */
|
||||
struct poly_inspector {
|
||||
/**
|
||||
* @brief Generic conversion operator (definition only).
|
||||
* @tparam Type Type to which conversion is requested.
|
||||
*/
|
||||
template <class Type>
|
||||
operator Type &&() const;
|
||||
|
||||
/**
|
||||
* @brief Dummy invocation function (definition only).
|
||||
* @tparam Member Index of the function to invoke.
|
||||
* @tparam Args Types of arguments to pass to the function.
|
||||
* @param args The arguments to pass to the function.
|
||||
* @return A poly inspector convertible to any type.
|
||||
*/
|
||||
template<auto Member, typename... Args>
|
||||
poly_inspector invoke(Args &&... args) const;
|
||||
|
||||
/*! @copydoc invoke */
|
||||
template<auto Member, typename... Args>
|
||||
poly_inspector invoke(Args &&... args);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Poly base class used to inject functionalities into concepts.
|
||||
* @tparam Poly The outermost poly class.
|
||||
*/
|
||||
template<typename Poly>
|
||||
struct poly_base {
|
||||
/**
|
||||
* @brief Invokes a function from the static virtual table.
|
||||
* @tparam Member Index of the function to invoke.
|
||||
* @tparam Args Types of arguments to pass to the function.
|
||||
* @param self A reference to the poly object that made the call.
|
||||
* @param args The arguments to pass to the function.
|
||||
* @return The return value of the invoked function, if any.
|
||||
*/
|
||||
template<auto Member, typename... Args>
|
||||
[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const {
|
||||
const auto &poly = static_cast<const Poly &>(self);
|
||||
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc invoke */
|
||||
template<auto Member, typename... Args>
|
||||
[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) {
|
||||
auto &poly = static_cast<Poly &>(self);
|
||||
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Shortcut for calling `poly_base<Type>::invoke`.
|
||||
* @tparam Member Index of the function to invoke.
|
||||
* @tparam Poly A fully defined poly object.
|
||||
* @tparam Args Types of arguments to pass to the function.
|
||||
* @param self A reference to the poly object that made the call.
|
||||
* @param args The arguments to pass to the function.
|
||||
* @return The return value of the invoked function, if any.
|
||||
*/
|
||||
template<auto Member, typename Poly, typename... Args>
|
||||
decltype(auto) poly_call(Poly &&self, Args &&... args) {
|
||||
return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Static polymorphism made simple and within everyone's reach.
|
||||
*
|
||||
* Static polymorphism is a very powerful tool in C++, albeit sometimes
|
||||
* cumbersome to obtain.<br/>
|
||||
* This class aims to make it simple and easy to use.
|
||||
*
|
||||
* Below is a minimal example of use:
|
||||
*
|
||||
* ```cpp
|
||||
* template<typename Base>
|
||||
* struct Drawable: Base {
|
||||
* void draw() { entt::poly_call<0>(*this); }
|
||||
* };
|
||||
*
|
||||
* template<typename Type>
|
||||
* inline constexpr auto entt::poly_impl<Drawable, Type> = entt::value_list<&Type::draw>{};
|
||||
*
|
||||
* using drawable = entt::poly<Drawable>;
|
||||
*
|
||||
* struct circle { void draw() {} };
|
||||
* struct square { void draw() {} };
|
||||
*
|
||||
* int main() {
|
||||
* drawable d{circle{}};
|
||||
* d.draw();
|
||||
*
|
||||
* d = square{};
|
||||
* d.draw();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The `poly` class template also supports aliasing for unmanaged objects.
|
||||
* Moreover, thanks to small buffer optimization, it limits the number of
|
||||
* allocations to a minimum where possible.
|
||||
*
|
||||
* @tparam Concept Concept class template.
|
||||
*/
|
||||
template<template<typename> class Concept>
|
||||
class poly: public Concept<poly_base<poly<Concept>>> {
|
||||
/*! @brief A poly base is allowed to snoop into a poly object. */
|
||||
friend struct poly_base<poly<Concept>>;
|
||||
|
||||
using vtable_t = std::remove_pointer_t<decltype(poly_vtable::instance<Concept, Concept<poly_inspector>>())>;
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
poly() ENTT_NOEXCEPT
|
||||
: storage{},
|
||||
vtable{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly by directly initializing the new object.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit poly(std::in_place_type_t<Type>, Args &&... args)
|
||||
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
|
||||
vtable{poly_vtable::instance<Concept, Type>()}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly that holds an unmanaged object.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @param value An instance of an object to use to initialize the poly.
|
||||
*/
|
||||
template<typename Type>
|
||||
poly(std::reference_wrapper<Type> value)
|
||||
: storage{value},
|
||||
vtable{poly_vtable::instance<Concept, Type>()}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly from a given value.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @param value An instance of an object to use to initialize the poly.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, poly>>>
|
||||
poly(Type &&value)
|
||||
: poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
poly(const poly &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
poly(poly &&other)
|
||||
: poly{}
|
||||
{
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment operator.
|
||||
* @param other The instance to assign from.
|
||||
* @return This poly object.
|
||||
*/
|
||||
poly & operator=(poly other) {
|
||||
swap(other, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type of the contained object.
|
||||
* @return The type of the contained object, if any.
|
||||
*/
|
||||
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
|
||||
return storage.type();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
|
||||
return storage.data();
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] void * data() ENTT_NOEXCEPT {
|
||||
return const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the contained object by creating a new instance directly.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&... args) {
|
||||
storage.emplace<Type>(std::forward<Args>(args)...);
|
||||
vtable = poly_vtable::instance<Concept, Type>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return A poly that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] poly ref() const ENTT_NOEXCEPT {
|
||||
poly other{};
|
||||
other.storage = storage.ref();
|
||||
other.vtable = vtable;
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if a poly is empty, true otherwise.
|
||||
* @return False if the poly is empty, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return !(vtable == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two poly objects.
|
||||
* @param lhs A valid poly object.
|
||||
* @param rhs A valid poly object.
|
||||
*/
|
||||
friend void swap(poly &lhs, poly &rhs) {
|
||||
using std::swap;
|
||||
swap(lhs.storage, rhs.storage);
|
||||
swap(lhs.vtable, rhs.vtable);
|
||||
}
|
||||
|
||||
private:
|
||||
any storage;
|
||||
const vtable_t *vtable;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -197,6 +197,10 @@ SETUP_BASIC_TEST(meta_prop entt/meta/meta_prop.cpp)
|
||||
SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
|
||||
SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
|
||||
|
||||
# Test poly
|
||||
|
||||
SETUP_BASIC_TEST(poly entt/poly/poly.cpp)
|
||||
|
||||
# Test process
|
||||
|
||||
SETUP_BASIC_TEST(process entt/process/process.cpp)
|
||||
|
||||
148
test/entt/poly/poly.cpp
Normal file
148
test/entt/poly/poly.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/poly/poly.hpp>
|
||||
|
||||
template<typename Base>
|
||||
struct concept: Base {
|
||||
void incr() { entt::poly_call<0>(*this); }
|
||||
void set(int v) { entt::poly_call<1>(*this, v); }
|
||||
int get() const { return entt::poly_call<2>(*this); }
|
||||
void decr() { entt::poly_call<3>(*this); }
|
||||
int mul(int v) { return entt::poly_call<4>(*this, v); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
void decr(Type &self) {
|
||||
self.set(self.get()-1);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
int mul(const Type &self, int v) {
|
||||
return v * self.get();
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr auto entt::poly_impl<concept, Type> =
|
||||
entt::value_list<
|
||||
&Type::incr,
|
||||
&Type::set,
|
||||
&Type::get,
|
||||
&decr<Type>,
|
||||
&mul<Type>
|
||||
>{};
|
||||
|
||||
struct impl {
|
||||
void incr() { ++value; }
|
||||
void set(int v) { value = v; }
|
||||
int get() const { return value; }
|
||||
int value{};
|
||||
};
|
||||
|
||||
TEST(Poly, Functionalities) {
|
||||
impl instance{};
|
||||
|
||||
entt::poly<concept> empty{};
|
||||
entt::poly<concept> in_place{std::in_place_type<impl>, 3};
|
||||
entt::poly<concept> alias{std::ref(instance)};
|
||||
entt::poly<concept> value{impl{}};
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
ASSERT_TRUE(in_place);
|
||||
ASSERT_TRUE(alias);
|
||||
ASSERT_TRUE(value);
|
||||
|
||||
ASSERT_EQ(empty.type(), entt::type_info{});
|
||||
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(alias.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(value.type(), entt::type_id<impl>());
|
||||
|
||||
ASSERT_EQ(alias.data(), &instance);
|
||||
ASSERT_EQ(std::as_const(alias).data(), &instance);
|
||||
|
||||
empty = impl{};
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_NE(empty.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(empty).data(), nullptr);
|
||||
ASSERT_EQ(empty.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(empty.get(), 0);
|
||||
|
||||
empty.emplace<impl>(3);
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_EQ(empty.get(), 3);
|
||||
|
||||
entt::poly<concept> ref = in_place.ref();
|
||||
|
||||
ASSERT_TRUE(ref);
|
||||
ASSERT_NE(ref.data(), nullptr);
|
||||
ASSERT_EQ(ref.data(), in_place.data());
|
||||
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
|
||||
ASSERT_EQ(ref.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(ref.get(), 3);
|
||||
|
||||
entt::poly<concept> null{};
|
||||
std::swap(empty, null);
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
|
||||
entt::poly<concept> copy = in_place;
|
||||
|
||||
ASSERT_TRUE(copy);
|
||||
ASSERT_EQ(copy.get(), 3);
|
||||
|
||||
entt::poly<concept> move = std::move(copy);
|
||||
|
||||
ASSERT_TRUE(move);
|
||||
ASSERT_FALSE(copy);
|
||||
ASSERT_EQ(move.get(), 3);
|
||||
}
|
||||
|
||||
TEST(Poly, Owned) {
|
||||
entt::poly<concept> poly{impl{}};
|
||||
auto *ptr = static_cast<impl *>(poly.data());
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(ptr->value, 0);
|
||||
ASSERT_EQ(poly.get(), 0);
|
||||
|
||||
poly.set(1);
|
||||
poly.incr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 2);
|
||||
ASSERT_EQ(poly.get(), 2);
|
||||
ASSERT_EQ(poly.mul(3), 6);
|
||||
|
||||
poly.decr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 1);
|
||||
ASSERT_EQ(poly.get(), 1);
|
||||
ASSERT_EQ(poly.mul(3), 3);
|
||||
}
|
||||
|
||||
TEST(Poly, Alias) {
|
||||
impl instance{};
|
||||
entt::poly<concept> poly{std::ref(instance)};
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(instance.value, 0);
|
||||
ASSERT_EQ(poly.get(), 0);
|
||||
|
||||
poly.set(1);
|
||||
poly.incr();
|
||||
|
||||
ASSERT_EQ(instance.value, 2);
|
||||
ASSERT_EQ(poly.get(), 2);
|
||||
ASSERT_EQ(poly.mul(3), 6);
|
||||
|
||||
poly.decr();
|
||||
|
||||
ASSERT_EQ(instance.value, 1);
|
||||
ASSERT_EQ(poly.get(), 1);
|
||||
ASSERT_EQ(poly.mul(3), 3);
|
||||
}
|
||||
Reference in New Issue
Block a user