blob: 55d839bb06fec186f28346987c541f44918625ec [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CONTAINERS_UNIQUE_ANY_H_
#define BASE_CONTAINERS_UNIQUE_ANY_H_
#include <utility>
#include "base/containers/any_internal.h"
// base::unique_any is similar to std::any except it:
// * can hold move constructible types such as std::unique_ptr<>
// * doesn't have a copy constructor or copy assignment operator
// * doesn't require exceptions or RTTI.
//
// It can be thought of as a better void * E.g.
// base::unique_any a = 123;
// EXPECT_EQ(123, base::unique_any_cast<int>(a));
//
// a = std::string("123");
// EXPECT_EQ("123", base::unique_any_cast<std::string>(a));
//
// a = make_unique_ptr("123");
// EXPECT_EQ("123", *base::unique_any_cast<std::unique_ptr<int>>(a));
//
// base::unique_any b(std::move(a));
// EXPECT_EQ("123", *base::unique_any_cast<std::unique_ptr<int>>(b));
//
// Note an incorrect base::unique_any_cast will lead to a CHECK.
namespace base {
class unique_any;
namespace internal {
template <typename T>
struct is_unique_any {
static constexpr bool value = false;
};
template <>
struct is_unique_any<unique_any> {
static constexpr bool value = true;
};
} // namespace internal
// Constructs a base::unique_any of type |T| with the given arguments.
template <typename T, typename... Args>
unique_any make_unique_any(Args&&... args);
// Overload of |base::make_unique_any()| for constructing a base::unique_any
// type from an initializer list. E.g.
// base::unique_any a = base::make_any<std::vector<int>>({1, 2, 3, 4});
template <typename T, typename U, typename... Args>
unique_any make_unique_any(std::initializer_list<U> il, Args&&... args);
// Statically casts the value of a const base::unique_any to the given type.
// Unlike std::any_cast which throws an exception, this function will CHECK if
// the stored value type |any| does not match the cast. E.g.
//
// base::unique_any a = 123;
// int i = base::unique_any_cast<int>(a); // i = 123
//
// NB unique_any_cast() can also be used to get a reference to the internal
// storage iff a reference type is passed as its ValueType. E.g.
//
// base::unique_any my_any = std::vector<int>();
// base::unique_any_cast<std::vector<int>&>(my_any).push_back(42);
//
template <typename ValueType>
ValueType unique_any_cast(const unique_any& any);
template <typename ValueType>
ValueType unique_any_cast(unique_any& any);
template <typename ValueType>
ValueType unique_any_cast(unique_any&& any);
// Overload of unique_any_cast() to statically cast the value of a const pointer
// base::unique_any to the given pointer type, or nullptr if the stored value
// type of |any| does not match the cast.
template <typename ValueType>
const ValueType* unique_any_cast(const unique_any* any) noexcept;
template <typename ValueType>
ValueType* unique_any_cast(unique_any* any) noexcept;
class BASE_EXPORT unique_any {
private:
template <typename T, bool UseInlineStorage>
using ConstructHelper =
internal::AnyInternal::ConstructHelper<T, UseInlineStorage>;
template <typename T>
using InlineStorageHelper = internal::AnyInternal::InlineStorageHelper<T>;
template <typename T>
using TypeOpsHelper = internal::AnyInternal::TypeOpsHelper<T>;
public:
// Constructs an empty base::unique_any.
constexpr unique_any() noexcept {}
// Constructs a base::unique_any with the value contained by |other| moved
// into it.
constexpr unique_any(unique_any&& other) noexcept
: internal_(other.internal_.type_ops_) {
if (internal_.type_ops_)
internal_.type_ops_->move_fn_ptr(&other.internal_, &internal_);
}
template <typename T>
struct is_move_or_copy_constructible {
static constexpr bool value = std::is_copy_constructible<T>::value ||
std::is_move_constructible<T>::value;
};
// Constructs a base::unique_any containing |value| as long as |T| isn't
// base::unique_any nor base::in_place_type_t<> and |T| is move or copy
// constructible. E.g. base::unique_any a(123);
template <
typename T,
typename VT = std::decay_t<T>,
std::enable_if_t<!internal::is_unique_any<VT>::value>* = nullptr,
std::enable_if_t<!is_in_place_type_t<VT>::value>* = nullptr,
std::enable_if_t<is_move_or_copy_constructible<VT>::value>* = nullptr>
unique_any(T&& value) noexcept : internal_(&TypeOpsHelper<VT>::type_ops) {
ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
&internal_, std::forward<T>(value));
}
// Constructs a base::unique_any containing an object of type T which is
// initialized by std::forward<Args>(args). E.g.
// base::unique_any a(base::in_place_type_t<std::unique_ptr<int>>(), 123);
template <
typename T,
typename... Args,
typename VT = std::decay_t<T>,
std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
std::is_constructible<VT, Args...>::value>* = nullptr>
explicit unique_any(in_place_type_t<T> /*tag*/, Args&&... args) noexcept
: internal_(&TypeOpsHelper<VT>::type_ops) {
ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
&internal_, std::forward<Args>(args)...);
}
// Constructs a base::unique_any containing an object of type T which is
// initialized with a std::initializer_list<U> and std::forward for any
// remaining args. E.g. base::unique_any
// a(base::in_place_type_t<std::vector<int>>(), {1, 2, 3}); base::unique_any
// b(base::in_place_type_t<std::vector<int>>(), {1, 2, 3}, 4, 5);
template <typename T,
typename U,
typename... Args,
typename VT = std::decay_t<T>,
std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
std::is_constructible<VT,
std::initializer_list<U>&,
Args...>::value>* = nullptr>
explicit unique_any(in_place_type_t<T> /*tag*/,
std::initializer_list<U> ilist,
Args&&... args) noexcept
: internal_(&internal::AnyInternal::TypeOpsHelper<VT>::type_ops) {
ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
&internal_, ilist, std::forward<Args>(args)...);
}
~unique_any();
// Emplaces a value within a base::unique_any object by calling 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.
// E.g.
// base::unique_any a;
// a.emplace<std::unique_ptr<int>>(123);
template <
typename T,
typename... Args,
typename VT = std::decay_t<T>,
std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
std::is_constructible<VT, Args...>::value>* = nullptr>
VT& emplace(Args&&... args) noexcept {
reset();
ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
&internal_, std::forward<Args>(args)...);
internal_.type_ops_ = &TypeOpsHelper<VT>::type_ops;
return *internal_.GetStorage<VT>();
}
// Overload of |emplace()| to emplace a value within a |base::unique_any|
// object by calling |reset()|, initializing the contained value as if
// direct-non-list-initializing an object of type |VT| with the arguments
// |initializer_list, std::forward<Args>(args)...|, and returning a reference
// to the new contained value.
// E.g.
// base::unique_any a;
// a.emplace<std::vector<int>>({1, 2, 3});
template <typename T,
class U,
typename... Args,
typename VT = std::decay_t<T>,
std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
std::is_constructible<VT,
std::initializer_list<U>&,
Args...>::value>* = nullptr>
VT& emplace(std::initializer_list<U> ilist, Args&&... args) noexcept {
reset();
ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
&internal_, ilist, std::forward<Args>(args)...);
internal_.type_ops_ = &TypeOpsHelper<VT>::type_ops;
return *internal_.GetStorage<VT>();
}
// Assigns |t| as long as |T| is move or copy constructible and it isn't
// base::unique_any.
template <typename T,
typename VT = std::decay_t<T>,
std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
!internal::is_unique_any<VT>::value>* = nullptr>
unique_any& operator=(T&& t) noexcept {
reset();
ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
&internal_, std::forward<T>(t));
internal_.type_ops_ = &TypeOpsHelper<VT>::type_ops;
return *this;
}
unique_any& operator=(unique_any&& other) noexcept {
internal_ = std::move(other.internal_);
return *this;
}
void swap(unique_any& other) noexcept {
using std::swap;
swap(internal_, other.internal_);
}
bool has_value() const noexcept { return internal_.has_value(); }
// Note unlike std::any we return TypeId which does not require RTTI.
TypeId type() const noexcept { return internal_.type(); }
void reset() noexcept { internal_.reset(); }
private:
template <typename ValueType>
friend ValueType unique_any_cast(const unique_any& any);
template <typename ValueType>
friend ValueType unique_any_cast(unique_any& any);
template <typename ValueType>
friend ValueType unique_any_cast(unique_any&& any);
template <typename ValueType>
friend const ValueType* unique_any_cast(const unique_any* any) noexcept;
template <typename ValueType>
friend ValueType* unique_any_cast(unique_any* any) noexcept;
internal::AnyInternal internal_;
};
// Swaps two base::Any values. Equivalent to |x.swap(y)| where |x| and |y| are
// base::Any types.
inline void swap(unique_any& x, unique_any& y) noexcept {
x.swap(y);
}
template <typename T, typename... Args>
unique_any make_unique_any(Args&&... args) {
return unique_any(in_place_type_t<T>(), std::forward<Args>(args)...);
}
template <typename T, typename U, typename... Args>
unique_any make_unique_any(std::initializer_list<U> il, Args&&... args) {
return unique_any(in_place_type_t<T>(), il, std::forward<Args>(args)...);
}
template <typename ValueType>
ValueType unique_any_cast(const unique_any& any) {
using U = typename std::remove_cv<
typename std::remove_reference<ValueType>::type>::type;
static_assert(std::is_constructible<ValueType, const U&>::value,
"Invalid ValueType");
DCHECK(any.has_value());
CHECK_EQ(TypeId::From<U>(), any.type());
return *any.internal_.GetStorage<U>();
}
template <typename ValueType>
ValueType unique_any_cast(unique_any& any) {
using U = typename std::remove_cv<
typename std::remove_reference<ValueType>::type>::type;
static_assert(std::is_constructible<ValueType, U&>::value,
"Invalid ValueType");
DCHECK(any.has_value());
CHECK_EQ(TypeId::From<U>(), any.type());
return *any.internal_.GetStorage<U>();
}
template <typename ValueType>
ValueType unique_any_cast(unique_any&& any) {
using U = typename std::remove_cv<
typename std::remove_reference<ValueType>::type>::type;
static_assert(std::is_constructible<ValueType, U>::value,
"Invalid ValueType");
DCHECK(any.has_value());
CHECK_EQ(TypeId::From<U>(), any.type());
return std::move(*any.internal_.GetStorage<U>());
}
template <typename ValueType>
const ValueType* unique_any_cast(const unique_any* any) noexcept {
using U = typename std::remove_cv<ValueType>::type;
if (TypeId::From<U>() != any->type())
return nullptr;
return any->internal_.GetStorage<U>();
}
template <typename ValueType>
ValueType* unique_any_cast(unique_any* any) noexcept {
using U = typename std::remove_cv<ValueType>::type;
if (TypeId::From<U>() != any->type())
return nullptr;
return any->internal_.GetStorage<U>();
}
} // namespace base
#endif // BASE_CONTAINERS_UNIQUE_ANY_H_