blob: 6ed0c6699c92efad86a9135ca4b0b76c528b06cf [file] [log] [blame]
// 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
//
// https://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.
//
#ifndef ABSL_TYPES_INTERNAL_OPTIONAL_H_
#define ABSL_TYPES_INTERNAL_OPTIONAL_H_
#include <functional>
#include <new>
#include <type_traits>
#include <utility>
#include "absl/base/internal/inline_variable.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/utility/utility.h"
// ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
//
// Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015.
// __cpp_inheriting_constructors is a predefined macro and a recommended way to
// check for this language feature, but GCC doesn't support it until 5.0 and
// Clang doesn't support it until 3.6.
// Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template
// constructor. For example, the following code won't work on MSVC 2015 Update3:
// struct Base {
// int t;
// template <typename T>
// constexpr Base(T t_) : t(t_) {}
// };
// struct Foo : Base {
// using Base::Base;
// }
// constexpr Foo foo(0); // doesn't work on MSVC 2015
#if defined(__clang__)
#if __has_feature(cxx_inheriting_constructors)
#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1
#endif
#elif (defined(__GNUC__) && \
(__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \
(__cpp_inheriting_constructors >= 200802) || \
(defined(_MSC_VER) && _MSC_VER >= 1910)
#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
// Forward declaration
template <typename T>
class optional;
namespace optional_internal {
// This tag type is used as a constructor parameter type for `nullopt_t`.
struct init_t {
explicit init_t() = default;
};
struct empty_struct {};
// This class stores the data in optional<T>.
// It is specialized based on whether T is trivially destructible.
// This is the specialization for non trivially destructible type.
template <typename T, bool unused = std::is_trivially_destructible<T>::value>
class optional_data_dtor_base {
struct dummy_type {
static_assert(sizeof(T) % sizeof(empty_struct) == 0, "");
// Use an array to avoid GCC 6 placement-new warning.
empty_struct data[sizeof(T) / sizeof(empty_struct)];
};
protected:
// Whether there is data or not.
bool engaged_;
// Data storage
union {
T data_;
dummy_type dummy_;
};
void destruct() noexcept {
if (engaged_) {
// `data_` must be initialized if `engaged_` is true.
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
data_.~T();
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
#pragma GCC diagnostic pop
#endif
engaged_ = false;
}
}
// dummy_ must be initialized for constexpr constructor.
constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {}
template <typename... Args>
constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args)
: engaged_(true), data_(absl::forward<Args>(args)...) {}
~optional_data_dtor_base() { destruct(); }
};
// Specialization for trivially destructible type.
template <typename T>
class optional_data_dtor_base<T, true> {
struct dummy_type {
static_assert(sizeof(T) % sizeof(empty_struct) == 0, "");
// Use array to avoid GCC 6 placement-new warning.
empty_struct data[sizeof(T) / sizeof(empty_struct)];
};
protected:
// Whether there is data or not.
bool engaged_;
// Data storage
union {
T data_;
dummy_type dummy_;
};
void destruct() noexcept { engaged_ = false; }
// dummy_ must be initialized for constexpr constructor.
constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {}
template <typename... Args>
constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args)
: engaged_(true), data_(absl::forward<Args>(args)...) {}
};
template <typename T>
class optional_data_base : public optional_data_dtor_base<T> {
protected:
using base = optional_data_dtor_base<T>;
#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
using base::base;
#else
optional_data_base() = default;
template <typename... Args>
constexpr explicit optional_data_base(in_place_t t, Args&&... args)
: base(t, absl::forward<Args>(args)...) {}
#endif
template <typename... Args>
void construct(Args&&... args) {
// Use dummy_'s address to work around casting cv-qualified T* to void*.
::new (static_cast<void*>(&this->dummy_)) T(std::forward<Args>(args)...);
this->engaged_ = true;
}
template <typename U>
void assign(U&& u) {
if (this->engaged_) {
this->data_ = std::forward<U>(u);
} else {
construct(std::forward<U>(u));
}
}
};
// TODO(absl-team): Add another class using
// std::is_trivially_move_constructible trait when available to match
// http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that
// have trivial move but nontrivial copy.
// Also, we should be checking is_trivially_copyable here, which is not
// supported now, so we use is_trivially_* traits instead.
template <typename T,
bool unused = absl::is_trivially_copy_constructible<T>::value&&
absl::is_trivially_copy_assignable<typename std::remove_cv<
T>::type>::value&& std::is_trivially_destructible<T>::value>
class optional_data;
// Trivially copyable types
template <typename T>
class optional_data<T, true> : public optional_data_base<T> {
protected:
#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
using optional_data_base<T>::optional_data_base;
#else
optional_data() = default;
template <typename... Args>
constexpr explicit optional_data(in_place_t t, Args&&... args)
: optional_data_base<T>(t, absl::forward<Args>(args)...) {}
#endif
};
template <typename T>
class optional_data<T, false> : public optional_data_base<T> {
protected:
#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
using optional_data_base<T>::optional_data_base;
#else
template <typename... Args>
constexpr explicit optional_data(in_place_t t, Args&&... args)
: optional_data_base<T>(t, absl::forward<Args>(args)...) {}
#endif
optional_data() = default;
optional_data(const optional_data& rhs) : optional_data_base<T>() {
if (rhs.engaged_) {
this->construct(rhs.data_);
}
}
optional_data(optional_data&& rhs) noexcept(
absl::default_allocator_is_nothrow::value ||
std::is_nothrow_move_constructible<T>::value)
: optional_data_base<T>() {
if (rhs.engaged_) {
this->construct(std::move(rhs.data_));
}
}
optional_data& operator=(const optional_data& rhs) {
if (rhs.engaged_) {
this->assign(rhs.data_);
} else {
this->destruct();
}
return *this;
}
optional_data& operator=(optional_data&& rhs) noexcept(
std::is_nothrow_move_assignable<T>::value&&
std::is_nothrow_move_constructible<T>::value) {
if (rhs.engaged_) {
this->assign(std::move(rhs.data_));
} else {
this->destruct();
}
return *this;
}
};
// Ordered by level of restriction, from low to high.
// Copyable implies movable.
enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 };
// Base class for enabling/disabling copy/move constructor.
template <copy_traits>
class optional_ctor_base;
template <>
class optional_ctor_base<copy_traits::copyable> {
public:
constexpr optional_ctor_base() = default;
optional_ctor_base(const optional_ctor_base&) = default;
optional_ctor_base(optional_ctor_base&&) = default;
optional_ctor_base& operator=(const optional_ctor_base&) = default;
optional_ctor_base& operator=(optional_ctor_base&&) = default;
};
template <>
class optional_ctor_base<copy_traits::movable> {
public:
constexpr optional_ctor_base() = default;
optional_ctor_base(const optional_ctor_base&) = delete;
optional_ctor_base(optional_ctor_base&&) = default;
optional_ctor_base& operator=(const optional_ctor_base&) = default;
optional_ctor_base& operator=(optional_ctor_base&&) = default;
};
template <>
class optional_ctor_base<copy_traits::non_movable> {
public:
constexpr optional_ctor_base() = default;
optional_ctor_base(const optional_ctor_base&) = delete;
optional_ctor_base(optional_ctor_base&&) = delete;
optional_ctor_base& operator=(const optional_ctor_base&) = default;
optional_ctor_base& operator=(optional_ctor_base&&) = default;
};
// Base class for enabling/disabling copy/move assignment.
template <copy_traits>
class optional_assign_base;
template <>
class optional_assign_base<copy_traits::copyable> {
public:
constexpr optional_assign_base() = default;
optional_assign_base(const optional_assign_base&) = default;
optional_assign_base(optional_assign_base&&) = default;
optional_assign_base& operator=(const optional_assign_base&) = default;
optional_assign_base& operator=(optional_assign_base&&) = default;
};
template <>
class optional_assign_base<copy_traits::movable> {
public:
constexpr optional_assign_base() = default;
optional_assign_base(const optional_assign_base&) = default;
optional_assign_base(optional_assign_base&&) = default;
optional_assign_base& operator=(const optional_assign_base&) = delete;
optional_assign_base& operator=(optional_assign_base&&) = default;
};
template <>
class optional_assign_base<copy_traits::non_movable> {
public:
constexpr optional_assign_base() = default;
optional_assign_base(const optional_assign_base&) = default;
optional_assign_base(optional_assign_base&&) = default;
optional_assign_base& operator=(const optional_assign_base&) = delete;
optional_assign_base& operator=(optional_assign_base&&) = delete;
};
template <typename T>
struct ctor_copy_traits {
static constexpr copy_traits traits =
std::is_copy_constructible<T>::value
? copy_traits::copyable
: std::is_move_constructible<T>::value ? copy_traits::movable
: copy_traits::non_movable;
};
template <typename T>
struct assign_copy_traits {
static constexpr copy_traits traits =
absl::is_copy_assignable<T>::value && std::is_copy_constructible<T>::value
? copy_traits::copyable
: absl::is_move_assignable<T>::value &&
std::is_move_constructible<T>::value
? copy_traits::movable
: copy_traits::non_movable;
};
// Whether T is constructible or convertible from optional<U>.
template <typename T, typename U>
struct is_constructible_convertible_from_optional
: std::integral_constant<
bool, std::is_constructible<T, optional<U>&>::value ||
std::is_constructible<T, optional<U>&&>::value ||
std::is_constructible<T, const optional<U>&>::value ||
std::is_constructible<T, const optional<U>&&>::value ||
std::is_convertible<optional<U>&, T>::value ||
std::is_convertible<optional<U>&&, T>::value ||
std::is_convertible<const optional<U>&, T>::value ||
std::is_convertible<const optional<U>&&, T>::value> {};
// Whether T is constructible or convertible or assignable from optional<U>.
template <typename T, typename U>
struct is_constructible_convertible_assignable_from_optional
: std::integral_constant<
bool, is_constructible_convertible_from_optional<T, U>::value ||
std::is_assignable<T&, optional<U>&>::value ||
std::is_assignable<T&, optional<U>&&>::value ||
std::is_assignable<T&, const optional<U>&>::value ||
std::is_assignable<T&, const optional<U>&&>::value> {};
// Helper function used by [optional.relops], [optional.comp_with_t],
// for checking whether an expression is convertible to bool.
bool convertible_to_bool(bool);
// Base class for std::hash<absl::optional<T>>:
// If std::hash<std::remove_const_t<T>> is enabled, it provides operator() to
// compute the hash; Otherwise, it is disabled.
// Reference N4659 23.14.15 [unord.hash].
template <typename T, typename = size_t>
struct optional_hash_base {
optional_hash_base() = delete;
optional_hash_base(const optional_hash_base&) = delete;
optional_hash_base(optional_hash_base&&) = delete;
optional_hash_base& operator=(const optional_hash_base&) = delete;
optional_hash_base& operator=(optional_hash_base&&) = delete;
};
template <typename T>
struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()(
std::declval<absl::remove_const_t<T> >()))> {
using argument_type = absl::optional<T>;
using result_type = size_t;
size_t operator()(const absl::optional<T>& opt) const {
absl::type_traits_internal::AssertHashEnabled<absl::remove_const_t<T>>();
if (opt) {
return std::hash<absl::remove_const_t<T> >()(*opt);
} else {
return static_cast<size_t>(0x297814aaad196e6dULL);
}
}
};
} // namespace optional_internal
ABSL_NAMESPACE_END
} // namespace absl
#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
#endif // ABSL_TYPES_INTERNAL_OPTIONAL_H_