diff options
author | misterg <misterg@google.com> | 2017-09-19T20·54-0400 |
---|---|---|
committer | misterg <misterg@google.com> | 2017-09-19T20·54-0400 |
commit | c2e754829628d1e9b7a16b3389cfdace76950fdf (patch) | |
tree | 5a7f056f44e27c30e10025113b644f0b3b5801fc /absl/types/any.h |
Initial Commit
Diffstat (limited to 'absl/types/any.h')
-rw-r--r-- | absl/types/any.h | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/absl/types/any.h b/absl/types/any.h new file mode 100644 index 000000000000..a51dea110d0f --- /dev/null +++ b/absl/types/any.h @@ -0,0 +1,539 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// any.h +// ----------------------------------------------------------------------------- +// +// This header file define the `absl::any` type for holding a type-safe value +// of any type. The 'absl::any` type is useful for providing a way to hold +// something that is, as yet, unspecified. Such unspecified types +// traditionally are passed between API boundaries until they are later cast to +// their "destination" types. To cast to such a destination type, use +// `absl::any_cast()`. Note that when casting an `absl::any`, you must cast it +// to an explicit type; implicit conversions will throw. +// +// Example: +// +// auto a = absl::any(65); +// absl::any_cast<int>(a); // 65 +// absl::any_cast<char>(a); // throws absl::bad_any_cast +// absl::any_cast<std::string>(a); // throws absl::bad_any_cast +// +// `absl::any` is a C++11 compatible version of the C++17 `std::any` abstraction +// and is designed to be a drop-in replacement for code compliant with C++17. +// +// Traditionally, the behavior of casting to a temporary unspecified type has +// been accomplished with the `void *` paradigm, where the pointer was to some +// other unspecified type. `absl::any` provides an "owning" version of `void *` +// that avoids issues of pointer management. +// +// Note: just as in the case of `void *`, use of `absl::any` (and its C++17 +// version `std::any`) is a code smell indicating that your API might not be +// constructed correctly. We have seen that most uses of `any` are unwarranted, +// and `absl::any`, like `std::any`, is difficult to use properly. Before using +// this abstraction, make sure that you should not instead be rewriting your +// code to be more specific. +// +// Abseil expects to release an `absl::variant` type shortly (a C++11 compatible +// version of the C++17 `std::variant), which is generally preferred for use +// over `absl::any`. +#ifndef ABSL_TYPES_ANY_H_ +#define ABSL_TYPES_ANY_H_ + +#include "absl/base/config.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_STD_ANY + +#include <any> + +namespace absl { +using std::any; +using std::any_cast; +using std::bad_any_cast; +using std::make_any; +} // namespace absl + +#else // ABSL_HAVE_STD_ANY + +#include <algorithm> +#include <cstddef> +#include <initializer_list> +#include <memory> +#include <stdexcept> +#include <type_traits> +#include <typeinfo> +#include <utility> + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/types/bad_any_cast.h" + +// NOTE: This macro is an implementation detail that is undefined at the bottom +// of the file. It is not intended for expansion directly from user code. +#ifdef ABSL_ANY_DETAIL_HAS_RTTI +#error ABSL_ANY_DETAIL_HAS_RTTI cannot be directly set +#elif !defined(__GNUC__) || defined(__GXX_RTTI) +#define ABSL_ANY_DETAIL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + +namespace absl { + +namespace any_internal { + +// FastTypeId<Type>() evaluates at compile/link-time to a unique integer for the +// passed in type. Their values are neither contiguous nor small, making them +// unfit for using as an index into a vector, but a good match for keys into +// maps or straight up comparisons. +// Note that on 64-bit (unix) systems size_t is 64-bit while int is 32-bit and +// the compiler will happily and quietly assign such a 64-bit value to a +// 32-bit integer. While a client should never do that it SHOULD still be safe, +// assuming the BSS segment doesn't span more than 4GiB. +template<typename Type> +inline size_t FastTypeId() { + static_assert(sizeof(char*) <= sizeof(size_t), + "ptr size too large for size_t"); + + // This static variable isn't actually used, only its address, so there are + // no concurrency issues. + static char dummy_var; + return reinterpret_cast<size_t>(&dummy_var); +} + +} // namespace any_internal + +class any; + +// swap() +// +// Swaps two `absl::any` values. Equivalent to `x.swap(y) where `x` and `y` are +// `absl::any` types. +void swap(any& x, any& y) noexcept; + +// make_any() +// +// Constructs an `absl::any` of type `T` with the given arguments. +template <typename T, typename... Args> +any make_any(Args&&... args); + +// Overload of `absl::make_any()` for constructing an `absl::any` type from an +// initializer list. +template <typename T, typename U, typename... Args> +any make_any(std::initializer_list<U> il, Args&&... args); + +// any_cast() +// +// Statically casts the value of a `const absl::any` type to the given type. +// This function will throw `absl::bad_any_cast` if the stored value type of the +// `absl::any` does not match the cast. +// +// `any_cast()` can also be used to get a reference to the internal storage iff +// a reference type is passed as its `ValueType`: +// +// Example: +// +// absl::any my_any = std::vector<int>(); +// absl::any_cast<std::vector<int>&>(my_any).push_back(42); +template <typename ValueType> +ValueType any_cast(const any& operand); + +// Overload of `any_cast()` to statically cast the value of a non-const +// `absl::any` type to the given type. This function will throw +// `absl::bad_any_cast` if the stored value type of the `absl::any` does not +// match the cast. +template <typename ValueType> +ValueType any_cast(any& operand); // NOLINT(runtime/references) + +// Overload of `any_cast()` to statically cast the rvalue of an `absl::any` +// type. This function will throw `absl::bad_any_cast` if the stored value type +// of the `absl::any` does not match the cast. +template <typename ValueType> +ValueType any_cast(any&& operand); + +// Overload of `any_cast()` to statically cast the value of a const pointer +// `absl::any` type to the given pointer type, or `nullptr` if the stored value +// type of the `absl::any` does not match the cast. +template <typename ValueType> +const ValueType* any_cast(const any* operand) noexcept; + +// Overload of `any_cast()` to statically cast the value of a pointer +// `absl::any` type to the given pointer type, or `nullptr` if the stored value +// type of the `absl::any` does not match the cast. +template <typename ValueType> +ValueType* any_cast(any* operand) noexcept; + +// any +// +// An `absl::any` object provides the facility to either store an instance of a +// type, known as the "contained object", or no value. An `absl::any` is used to +// store values of types that are unknown at compile time. The `absl::any` +// object, when containing a value, must contain a value type; storing a +// reference type is neither desired nor supported. +// +// An `absl::any` can only store a type that is copy-constructable; move-only +// types are not allowed within an `any` object. +// +// Example: +// +// auto a = absl::any(65); // Literal, copyable +// auto b = absl::any(std::vector<int>()); // Default-initialized, copyable +// std::unique_ptr<Foo> my_foo; +// auto c = absl::any(std::move(my_foo)); // Error, not copy-constructable +// +// Note that `absl::any` makes use of decayed types (`absl::decay_t` in this +// context) to remove const-volative qualifiers (known as "cv qualifiers"), +// decay functions to function pointers, etc. We essentially "decay" a given +// type into its essential type. +// +// `absl::any` makes use of decayed types when determing the basic type `T` of +// the value to store in the any's contained object. In the documentation below, +// we explcitly denote this by using the phrase "a decayed type of `T`". +// +// Example: +// +// const int a = 4; +// absl::any foo(a); // Decay ensures we store an "int", not a "const int&". +// +// void my_function() {} +// absl::any bar(my_function); // Decay ensures we store a function pointer. +// +// `absl::any` is a C++11 compatible version of the C++17 `std::any` abstraction +// and is designed to be a drop-in replacement for code compliant with C++17. +class any { + private: + template <typename T> + struct IsInPlaceType; + + public: + // Constructors + + // Constructs an empty `absl::any` object (`any::has_value()` will return + // `false`). + constexpr any() noexcept; + + // Copy constructs an `absl::any` object with a "contained object" of the + // passed type of `other` (or an empty `absl::any` if `other.has_value()` is + // `false`. + any(const any& other) + : obj_(other.has_value() ? other.obj_->Clone() + : std::unique_ptr<ObjInterface>()) {} + + // Move constructs an `absl::any` object with a "contained object" of the + // passed type of `other` (or an empty `absl::any` if `other.has_value()` is + // `false`). + any(any&& other) noexcept = default; + + // Constructs an `absl::any` object with a "contained object" of the decayed + // type of `T`, which is initialized via `std::forward<T>(value)`. + // + // This constructor will not participate in overload resolution if the + // decayed type of `T` is not copy-constructible. + template < + typename T, typename VT = absl::decay_t<T>, + absl::enable_if_t<!absl::disjunction< + std::is_same<any, VT>, IsInPlaceType<VT>, + absl::negation<std::is_copy_constructible<VT> > >::value>* = nullptr> + any(T&& value) : obj_(new Obj<VT>(in_place, std::forward<T>(value))) {} + + // Constructs an `absl::any` object with a "contained object" of the decayed + // type of `T`, which is initialized via `std::forward<T>(value)`. + template <typename T, typename... Args, typename VT = absl::decay_t<T>, + absl::enable_if_t<absl::conjunction< + std::is_copy_constructible<VT>, + std::is_constructible<VT, Args...>>::value>* = nullptr> + explicit any(in_place_type_t<T> /*tag*/, Args&&... args) + : obj_(new Obj<VT>(in_place, std::forward<Args>(args)...)) {} + + // Constructs an `absl::any` object with a "contained object" of the passed + // type `VT` as a decayed type of `T`. `VT` is initialized as if + // direct-non-list-initializing an object of type `VT` with the arguments + // `initializer_list, std::forward<Args>(args)...`. + template < + typename T, typename U, typename... Args, typename VT = absl::decay_t<T>, + absl::enable_if_t< + absl::conjunction<std::is_copy_constructible<VT>, + std::is_constructible<VT, std::initializer_list<U>&, + Args...>>::value>* = nullptr> + explicit any(in_place_type_t<T> /*tag*/, std::initializer_list<U> ilist, + Args&&... args) + : obj_(new Obj<VT>(in_place, ilist, std::forward<Args>(args)...)) {} + + // Assignment operators + + // Copy assigns an `absl::any` object with a "contained object" of the + // passed type. + any& operator=(const any& rhs) { + any(rhs).swap(*this); + return *this; + } + + // Move assigns an `absl::any` object with a "contained object" of the + // passed type. `rhs` is left in a valid but otherwise unspecified state. + any& operator=(any&& rhs) noexcept { + any(std::move(rhs)).swap(*this); + return *this; + } + + // Assigns an `absl::any` object with a "contained object" of the passed type. + template <typename T, typename VT = absl::decay_t<T>, + absl::enable_if_t<absl::conjunction< + absl::negation<std::is_same<VT, any>>, + std::is_copy_constructible<VT>>::value>* = nullptr> + any& operator=(T&& rhs) { + any tmp(in_place_type_t<VT>(), std::forward<T>(rhs)); + tmp.swap(*this); + return *this; + } + + // Modifiers + + // any::emplace() + // + // Emplaces a value within an `absl::any` object by calling `any::reset()`, + // initializing the contained value as if direct-non-list-initializing an + // object of type `VT` with the arguments `std::forward<Args>(args)...`, and + // returning a reference to the new contained value. + // + // Note: If an exception is thrown during the call to `VT`’s constructor, + // `*this` does not contain a value, and any previously contained value has + // been destroyed. + template < + typename T, typename... Args, typename VT = absl::decay_t<T>, + absl::enable_if_t<std::is_copy_constructible<VT>::value && + std::is_constructible<VT, Args...>::value>* = nullptr> + VT& emplace(Args&&... args) { + reset(); // NOTE: reset() is required here even in the world of exceptions. + Obj<VT>* const object_ptr = + new Obj<VT>(in_place, std::forward<Args>(args)...); + obj_ = std::unique_ptr<ObjInterface>(object_ptr); + return object_ptr->value; + } + + // Overload of `any::emplace()` to emplace a value within an `absl::any` + // object by calling `any::reset()`, initializing the contained value as if + // direct-non-list-initializing an object of type `VT` with the arguments + // `initilizer_list, std::forward<Args>(args)...`, and returning a reference + // to the new contained value. + // + // Note: If an exception is thrown during the call to `VT`’s constructor, + // `*this` does not contain a value, and any previously contained value has + // been destroyed. The function shall not participate in overload resolution + // unless `is_copy_constructible_v<VT>` is `true` and + // `is_constructible_v<VT, initializer_list<U>&, Args...>` is `true`. + template < + typename T, typename U, typename... Args, typename VT = absl::decay_t<T>, + absl::enable_if_t<std::is_copy_constructible<VT>::value && + std::is_constructible<VT, std::initializer_list<U>&, + Args...>::value>* = nullptr> + VT& emplace(std::initializer_list<U> ilist, Args&&... args) { + reset(); // NOTE: reset() is required here even in the world of exceptions. + Obj<VT>* const object_ptr = + new Obj<VT>(in_place, ilist, std::forward<Args>(args)...); + obj_ = std::unique_ptr<ObjInterface>(object_ptr); + return object_ptr->value; + } + + // any::reset() + // + // Resets the state of the `absl::any` object, destroying the contained object + // if present. + void reset() noexcept { obj_ = nullptr; } + + // any::swap() + // + // Swaps the passed value and the value of this `absl::any` object. + void swap(any& other) noexcept { obj_.swap(other.obj_); } + + // Observors + + // any::has_value() + // + // Returns `true` if the `any` object has a contained value, otherwise + // returns `false`. + bool has_value() const noexcept { return obj_ != nullptr; } + +#if ABSL_ANY_DETAIL_HAS_RTTI + // Returns: typeid(T) if *this has a contained object of type T, otherwise + // typeid(void). + const std::type_info& type() const noexcept { + if (has_value()) { + return obj_->Type(); + } + + return typeid(void); + } +#endif // ABSL_ANY_DETAIL_HAS_RTTI + private: + // Tagged type-erased abstraction for holding a cloneable object. + class ObjInterface { + public: + virtual ~ObjInterface() = default; + virtual std::unique_ptr<ObjInterface> Clone() const = 0; + virtual size_t type_id() const noexcept = 0; +#if ABSL_ANY_DETAIL_HAS_RTTI + virtual const std::type_info& Type() const noexcept = 0; +#endif // ABSL_ANY_DETAIL_HAS_RTTI + }; + + // Hold a value of some queryable type, with an ability to Clone it. + template <typename T> + class Obj : public ObjInterface { + public: + template <typename... Args> + explicit Obj(in_place_t /*tag*/, Args&&... args) + : value(std::forward<Args>(args)...) {} + + std::unique_ptr<ObjInterface> Clone() const final { + return std::unique_ptr<ObjInterface>(new Obj(in_place, value)); + } + + size_t type_id() const noexcept final { return IdForType<T>(); } + +#if ABSL_ANY_DETAIL_HAS_RTTI + const std::type_info& Type() const noexcept final { return typeid(T); } +#endif // ABSL_ANY_DETAIL_HAS_RTTI + + T value; + }; + + std::unique_ptr<ObjInterface> CloneObj() const { + if (!obj_) return nullptr; + return obj_->Clone(); + } + + template <typename T> + static size_t IdForType() { + // Note: This type dance is to make the behavior consistent with typeid. + using NormalizedType = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; + + return any_internal::FastTypeId<NormalizedType>(); + } + + size_t GetObjTypeId() const { + return obj_ == nullptr ? any_internal::FastTypeId<void>() : obj_->type_id(); + } + + // `absl::any` nonmember functions // + + // Description at the declaration site (top of file). + template <typename ValueType> + friend ValueType any_cast(const any& operand); + + // Description at the declaration site (top of file). + template <typename ValueType> + friend ValueType any_cast(any& operand); // NOLINT(runtime/references) + + // Description at the declaration site (top of file). + template <typename T> + friend const T* any_cast(const any* operand) noexcept; + + // Description at the declaration site (top of file). + template <typename T> + friend T* any_cast(any* operand) noexcept; + + std::unique_ptr<ObjInterface> obj_; +}; + +// ----------------------------------------------------------------------------- +// Implementation Details +// ----------------------------------------------------------------------------- + +constexpr any::any() noexcept = default; + +template <typename T> +struct any::IsInPlaceType : std::false_type {}; + +template <typename T> +struct any::IsInPlaceType<in_place_type_t<T>> : std::true_type {}; + +inline void swap(any& x, any& y) noexcept { x.swap(y); } + +// Description at the declaration site (top of file). +template <typename T, typename... Args> +any make_any(Args&&... args) { + return any(in_place_type_t<T>(), std::forward<Args>(args)...); +} + +// Description at the declaration site (top of file). +template <typename T, typename U, typename... Args> +any make_any(std::initializer_list<U> il, Args&&... args) { + return any(in_place_type_t<T>(), il, std::forward<Args>(args)...); +} + +// Description at the declaration site (top of file). +template <typename ValueType> +ValueType any_cast(const any& operand) { + using U = typename std::remove_cv< + typename std::remove_reference<ValueType>::type>::type; + static_assert(std::is_constructible<ValueType, const U&>::value, + "Invalid ValueType"); + auto* const result = (any_cast<U>)(&operand); + if (result == nullptr) { + any_internal::ThrowBadAnyCast(); + } + return static_cast<ValueType>(*result); +} + +// Description at the declaration site (top of file). +template <typename ValueType> +ValueType any_cast(any& operand) { // NOLINT(runtime/references) + using U = typename std::remove_cv< + typename std::remove_reference<ValueType>::type>::type; + static_assert(std::is_constructible<ValueType, U&>::value, + "Invalid ValueType"); + auto* result = (any_cast<U>)(&operand); + if (result == nullptr) { + any_internal::ThrowBadAnyCast(); + } + return static_cast<ValueType>(*result); +} + +// Description at the declaration site (top of file). +template <typename ValueType> +ValueType any_cast(any&& operand) { + using U = typename std::remove_cv< + typename std::remove_reference<ValueType>::type>::type; + static_assert(std::is_constructible<ValueType, U>::value, + "Invalid ValueType"); + return static_cast<ValueType>(std::move((any_cast<U&>)(operand))); +} + +// Description at the declaration site (top of file). +template <typename T> +const T* any_cast(const any* operand) noexcept { + return operand && operand->GetObjTypeId() == any::IdForType<T>() + ? std::addressof( + static_cast<const any::Obj<T>*>(operand->obj_.get())->value) + : nullptr; +} + +// Description at the declaration site (top of file). +template <typename T> +T* any_cast(any* operand) noexcept { + return operand && operand->GetObjTypeId() == any::IdForType<T>() + ? std::addressof( + static_cast<any::Obj<T>*>(operand->obj_.get())->value) + : nullptr; +} + +} // namespace absl + +#undef ABSL_ANY_DETAIL_HAS_RTTI + +#endif // ABSL_HAVE_STD_ANY + +#endif // ABSL_TYPES_ANY_H_ |