536 lines
20 KiB
C++
536 lines
20 KiB
C++
//
|
||
// 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 {
|
||
|
||
template <typename Type>
|
||
struct TypeTag {
|
||
constexpr static char dummy_var = 0;
|
||
};
|
||
|
||
template <typename Type>
|
||
constexpr char TypeTag<Type>::dummy_var;
|
||
|
||
// FastTypeId<Type>() evaluates at compile/link-time to a unique pointer for the
|
||
// passed in type. These are meant to be good match for keys into maps or
|
||
// straight up comparisons.
|
||
template<typename Type>
|
||
constexpr inline const void* FastTypeId() {
|
||
return &TypeTag<Type>::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 const void* ObjTypeId() 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));
|
||
}
|
||
|
||
const void* ObjTypeId() 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>
|
||
constexpr static const void* 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>();
|
||
}
|
||
|
||
const void* GetObjTypeId() const {
|
||
return obj_ ? obj_->ObjTypeId() : any_internal::FastTypeId<void>();
|
||
}
|
||
|
||
// `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_
|