any: doc + more tests

This commit is contained in:
Michele Caini
2020-11-30 10:08:26 +01:00
parent 5af7e96f70
commit 83322664f1
3 changed files with 108 additions and 28 deletions

View File

@@ -13,6 +13,7 @@
namespace entt {
/*! @brief A type-safe container for single values of any type. */
class any {
enum class operation { COPY, MOVE, DTOR, ADDR, REF, TYPE };
@@ -99,6 +100,12 @@ public:
instance{}
{}
/**
* @brief Constructs an any by directly initializing the new object.
* @tparam Type Type of object to use to initialize the wrapper.
* @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 any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
: vtable{&basic_vtable<Type>},
@@ -113,17 +120,31 @@ public:
}
}
/**
* @brief Constructs an any that holds an unmanaged object.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type>
any(std::reference_wrapper<Type> value)
: vtable{&basic_vtable<std::add_lvalue_reference_t<Type>>},
instance{&value.get()}
{}
/**
* @brief Constructs an any from a given value.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, any>>>
any(Type &&value)
: any{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.
*/
any(const any &other)
: any{}
{
@@ -131,6 +152,10 @@ public:
vtable(operation::COPY, other, this);
}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
any(any &&other) ENTT_NOEXCEPT
: any{}
{
@@ -138,44 +163,78 @@ public:
vtable(operation::MOVE, other, this);
}
/*! @brief Frees the internal storage, whatever it means. */
~any() {
vtable(operation::DTOR, *this, nullptr);
}
/**
* @brief Assignment operator.
* @param other The instance to assign from.
* @return This any any object.
*/
any & operator=(any other) {
swap(*this, other);
return *this;
}
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
return vtable(operation::ADDR, *this, nullptr);
}
[[nodiscard]] void * data() ENTT_NOEXCEPT {
return const_cast<void *>(std::as_const(*this).data());
}
template<typename Type, typename... Args>
void emplace(Args &&... args) {
*this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
}
[[nodiscard]] any ref() const ENTT_NOEXCEPT {
any other{};
vtable(operation::REF, *this, &other);
return other;
}
/**
* @brief Returns the type of the contained object.
* @return The type of the contained object, if any.
*/
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
type_info info;
vtable(operation::TYPE, *this, &info);
return info;
}
/**
* @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 vtable(operation::ADDR, *this, nullptr);
}
/*! @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 wrapper.
* @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) {
*this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
}
/**
* @brief Aliasing constructor.
* @return An any that shares a reference to an unmanaged object.
*/
[[nodiscard]] any ref() const ENTT_NOEXCEPT {
any other{};
vtable(operation::REF, *this, &other);
return other;
}
/**
* @brief Returns false if a wrapper is empty, true otherwise.
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(vtable(operation::ADDR, *this, nullptr) == nullptr);
}
/**
* @brief Swaps two any objects.
* @param lhs A valid any object.
* @param rhs A valid any object.
*/
friend void swap(any &lhs, any &rhs) {
any tmp{};
lhs.vtable(operation::MOVE, lhs, &tmp);
@@ -190,33 +249,45 @@ private:
};
/**
* @brief Performs type-safe access to the contained object.
* @param any Target any object.
* @return The element converted to the requested type.
*/
template<typename Type>
Type any_cast(const any &any) ENTT_NOEXCEPT {
ENTT_ASSERT(any.type() == type_id<Type>());
return *static_cast<const Type *>(any.data());
auto *instance = any_cast<std::remove_cv_t<std::remove_reference_t<Type>>>(&any);
ENTT_ASSERT(instance);
return static_cast<Type>(*instance);
}
/*! @copydoc any_cast */
template<typename Type>
Type any_cast(any &any) ENTT_NOEXCEPT {
ENTT_ASSERT(any.type() == type_id<Type>());
return *static_cast<Type *>(any.data());
auto *instance = any_cast<std::remove_cv_t<std::remove_reference_t<Type>>>(&any);
ENTT_ASSERT(instance);
return static_cast<Type>(*instance);
}
/*! @copydoc any_cast */
template<typename Type>
Type any_cast(any &&any) ENTT_NOEXCEPT {
ENTT_ASSERT(any.type() == type_id<Type>());
return std::move(*static_cast<Type *>(any.data()));
auto *instance = any_cast<std::remove_cv_t<std::remove_reference_t<Type>>>(&any);
ENTT_ASSERT(instance);
return static_cast<Type>(std::move(*instance));
}
/*! @copydoc any_cast */
template<typename Type>
const Type * any_cast(const any *any) ENTT_NOEXCEPT {
return (any->type() == type_id<Type>() ? static_cast<const Type *>(any->data()) : nullptr);
}
/*! @copydoc any_cast */
template<typename Type>
Type * any_cast(any *any) ENTT_NOEXCEPT {
return (any->type() == type_id<Type>() ? static_cast<Type *>(any->data()) : nullptr);

View File

@@ -476,8 +476,8 @@ public:
}
/**
* @brief Returns false if a wrapper is empty, true otherwise.
* @return False if the wrapper is empty, true otherwise.
* @brief Returns false if a wrapper is invalid, true otherwise.
* @return False if the wrapper is invalid, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(node == nullptr);

View File

@@ -540,6 +540,15 @@ TEST(Any, NoSBOWithVoidSwap) {
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{{.1, .2, .3, .4}}));
}
TEST(Any, AnyCastTemporary) {
TEST(Any, AnyCast) {
entt::any any{42};
const auto &cany = any;
ASSERT_EQ(entt::any_cast<char>(&any), nullptr);
ASSERT_EQ(entt::any_cast<char>(&cany), nullptr);
ASSERT_EQ(*entt::any_cast<int>(&any), 42);
ASSERT_EQ(*entt::any_cast<int>(&cany), 42);
ASSERT_EQ(entt::any_cast<int &>(any), 42);
ASSERT_EQ(entt::any_cast<const int &>(cany), 42);
ASSERT_EQ(entt::any_cast<int>(42), 42);
}