From c2e754829628d1e9b7a16b3389cfdace76950fdf Mon Sep 17 00:00:00 2001 From: misterg Date: Tue, 19 Sep 2017 16:54:40 -0400 Subject: Initial Commit --- absl/types/BUILD.bazel | 178 +++++ absl/types/any.h | 539 +++++++++++++ absl/types/any_test.cc | 713 +++++++++++++++++ absl/types/bad_any_cast.cc | 40 + absl/types/bad_any_cast.h | 44 ++ absl/types/bad_optional_access.cc | 42 + absl/types/bad_optional_access.h | 37 + absl/types/optional.cc | 24 + absl/types/optional.h | 1092 ++++++++++++++++++++++++++ absl/types/optional_test.cc | 1539 +++++++++++++++++++++++++++++++++++++ absl/types/span.h | 738 ++++++++++++++++++ absl/types/span_test.cc | 783 +++++++++++++++++++ 12 files changed, 5769 insertions(+) create mode 100644 absl/types/BUILD.bazel create mode 100644 absl/types/any.h create mode 100644 absl/types/any_test.cc create mode 100644 absl/types/bad_any_cast.cc create mode 100644 absl/types/bad_any_cast.h create mode 100644 absl/types/bad_optional_access.cc create mode 100644 absl/types/bad_optional_access.h create mode 100644 absl/types/optional.cc create mode 100644 absl/types/optional.h create mode 100644 absl/types/optional_test.cc create mode 100644 absl/types/span.h create mode 100644 absl/types/span_test.cc (limited to 'absl/types') diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel new file mode 100644 index 00000000..8d09440e --- /dev/null +++ b/absl/types/BUILD.bazel @@ -0,0 +1,178 @@ +# +# 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. +# + +load( + "//absl:copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_TEST_COPTS", + "ABSL_EXCEPTIONS_FLAG", +) +load( + "//absl:test_dependencies.bzl", + "GUNIT_MAIN_DEPS_SELECTOR", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "any", + hdrs = ["any.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":bad_any_cast", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/utility", + ], +) + +cc_library( + name = "bad_any_cast", + srcs = ["bad_any_cast.cc"], + hdrs = ["bad_any_cast.h"], + copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS, + features = [ + "-use_header_modules", # b/33207452 + ], + deps = [ + "//absl/base", + "//absl/base:config", + ], +) + +cc_test( + name = "any_test", + size = "small", + srcs = [ + "any_test.cc", + ], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":any", + "//absl/base", + "//absl/base:config", + "//absl/base:exception_testing", + "//absl/container:test_instance_tracker", + ] + select(GUNIT_MAIN_DEPS_SELECTOR), +) + +cc_test( + name = "any_test_noexceptions", + size = "small", + srcs = [ + "any_test.cc", + ], + copts = ABSL_TEST_COPTS, + deps = [ + ":any", + "//absl/base", + "//absl/base:config", + "//absl/base:exception_testing", + "//absl/container:test_instance_tracker", + ] + select(GUNIT_MAIN_DEPS_SELECTOR), +) + +cc_library( + name = "span", + hdrs = ["span.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/algorithm", + "//absl/base:core_headers", + "//absl/base:throw_delegate", + "//absl/meta:type_traits", + "//absl/strings", + ], +) + +cc_test( + name = "span_test", + size = "small", + srcs = ["span_test.cc"], + copts = ABSL_TEST_COPTS + ["-fexceptions"], + deps = [ + ":span", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:exception_testing", + "//absl/container:fixed_array", + "//absl/container:inlined_vector", + "//absl/strings", + ] + select(GUNIT_MAIN_DEPS_SELECTOR), +) + +cc_test( + name = "span_test_noexceptions", + size = "small", + srcs = ["span_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":span", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:exception_testing", + "//absl/container:fixed_array", + "//absl/container:inlined_vector", + "//absl/strings", + ] + select(GUNIT_MAIN_DEPS_SELECTOR), +) + +cc_library( + name = "optional", + srcs = ["optional.cc"], + hdrs = ["optional.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":bad_optional_access", + "//absl/base:config", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/utility", + ], +) + +cc_library( + name = "bad_optional_access", + srcs = ["bad_optional_access.cc"], + hdrs = ["bad_optional_access.h"], + copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, + features = [ + "-use_header_modules", # b/33207452 + ], + deps = [ + "//absl/base", + "//absl/base:config", + ], +) + +cc_test( + name = "optional_test", + size = "small", + srcs = [ + "optional_test.cc", + ], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":optional", + "//absl/base", + "//absl/base:config", + "//absl/meta:type_traits", + "//absl/strings", + ] + select(GUNIT_MAIN_DEPS_SELECTOR), +) diff --git a/absl/types/any.h b/absl/types/any.h new file mode 100644 index 00000000..a51dea11 --- /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(a); // 65 +// absl::any_cast(a); // throws absl::bad_any_cast +// absl::any_cast(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 + +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 +#include +#include +#include +#include +#include +#include +#include + +#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() 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 +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(&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 +any make_any(Args&&... args); + +// Overload of `absl::make_any()` for constructing an `absl::any` type from an +// initializer list. +template +any make_any(std::initializer_list 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(); +// absl::any_cast&>(my_any).push_back(42); +template +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 +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 +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 +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 +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()); // Default-initialized, copyable +// std::unique_ptr 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 + 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()) {} + + // 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(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, + absl::enable_if_t, IsInPlaceType, + absl::negation > >::value>* = nullptr> + any(T&& value) : obj_(new Obj(in_place, std::forward(value))) {} + + // Constructs an `absl::any` object with a "contained object" of the decayed + // type of `T`, which is initialized via `std::forward(value)`. + template , + absl::enable_if_t, + std::is_constructible>::value>* = nullptr> + explicit any(in_place_type_t /*tag*/, Args&&... args) + : obj_(new Obj(in_place, std::forward(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)...`. + template < + typename T, typename U, typename... Args, typename VT = absl::decay_t, + absl::enable_if_t< + absl::conjunction, + std::is_constructible&, + Args...>>::value>* = nullptr> + explicit any(in_place_type_t /*tag*/, std::initializer_list ilist, + Args&&... args) + : obj_(new Obj(in_place, ilist, std::forward(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 , + absl::enable_if_t>, + std::is_copy_constructible>::value>* = nullptr> + any& operator=(T&& rhs) { + any tmp(in_place_type_t(), std::forward(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)...`, 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, + absl::enable_if_t::value && + std::is_constructible::value>* = nullptr> + VT& emplace(Args&&... args) { + reset(); // NOTE: reset() is required here even in the world of exceptions. + Obj* const object_ptr = + new Obj(in_place, std::forward(args)...); + obj_ = std::unique_ptr(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)...`, 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` is `true` and + // `is_constructible_v&, Args...>` is `true`. + template < + typename T, typename U, typename... Args, typename VT = absl::decay_t, + absl::enable_if_t::value && + std::is_constructible&, + Args...>::value>* = nullptr> + VT& emplace(std::initializer_list ilist, Args&&... args) { + reset(); // NOTE: reset() is required here even in the world of exceptions. + Obj* const object_ptr = + new Obj(in_place, ilist, std::forward(args)...); + obj_ = std::unique_ptr(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 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 + class Obj : public ObjInterface { + public: + template + explicit Obj(in_place_t /*tag*/, Args&&... args) + : value(std::forward(args)...) {} + + std::unique_ptr Clone() const final { + return std::unique_ptr(new Obj(in_place, value)); + } + + size_t type_id() const noexcept final { return IdForType(); } + +#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 CloneObj() const { + if (!obj_) return nullptr; + return obj_->Clone(); + } + + template + static size_t IdForType() { + // Note: This type dance is to make the behavior consistent with typeid. + using NormalizedType = + typename std::remove_cv::type>::type; + + return any_internal::FastTypeId(); + } + + size_t GetObjTypeId() const { + return obj_ == nullptr ? any_internal::FastTypeId() : obj_->type_id(); + } + + // `absl::any` nonmember functions // + + // Description at the declaration site (top of file). + template + friend ValueType any_cast(const any& operand); + + // Description at the declaration site (top of file). + template + friend ValueType any_cast(any& operand); // NOLINT(runtime/references) + + // Description at the declaration site (top of file). + template + friend const T* any_cast(const any* operand) noexcept; + + // Description at the declaration site (top of file). + template + friend T* any_cast(any* operand) noexcept; + + std::unique_ptr obj_; +}; + +// ----------------------------------------------------------------------------- +// Implementation Details +// ----------------------------------------------------------------------------- + +constexpr any::any() noexcept = default; + +template +struct any::IsInPlaceType : std::false_type {}; + +template +struct any::IsInPlaceType> : std::true_type {}; + +inline void swap(any& x, any& y) noexcept { x.swap(y); } + +// Description at the declaration site (top of file). +template +any make_any(Args&&... args) { + return any(in_place_type_t(), std::forward(args)...); +} + +// Description at the declaration site (top of file). +template +any make_any(std::initializer_list il, Args&&... args) { + return any(in_place_type_t(), il, std::forward(args)...); +} + +// Description at the declaration site (top of file). +template +ValueType any_cast(const any& operand) { + using U = typename std::remove_cv< + typename std::remove_reference::type>::type; + static_assert(std::is_constructible::value, + "Invalid ValueType"); + auto* const result = (any_cast)(&operand); + if (result == nullptr) { + any_internal::ThrowBadAnyCast(); + } + return static_cast(*result); +} + +// Description at the declaration site (top of file). +template +ValueType any_cast(any& operand) { // NOLINT(runtime/references) + using U = typename std::remove_cv< + typename std::remove_reference::type>::type; + static_assert(std::is_constructible::value, + "Invalid ValueType"); + auto* result = (any_cast)(&operand); + if (result == nullptr) { + any_internal::ThrowBadAnyCast(); + } + return static_cast(*result); +} + +// Description at the declaration site (top of file). +template +ValueType any_cast(any&& operand) { + using U = typename std::remove_cv< + typename std::remove_reference::type>::type; + static_assert(std::is_constructible::value, + "Invalid ValueType"); + return static_cast(std::move((any_cast)(operand))); +} + +// Description at the declaration site (top of file). +template +const T* any_cast(const any* operand) noexcept { + return operand && operand->GetObjTypeId() == any::IdForType() + ? std::addressof( + static_cast*>(operand->obj_.get())->value) + : nullptr; +} + +// Description at the declaration site (top of file). +template +T* any_cast(any* operand) noexcept { + return operand && operand->GetObjTypeId() == any::IdForType() + ? std::addressof( + static_cast*>(operand->obj_.get())->value) + : nullptr; +} + +} // namespace absl + +#undef ABSL_ANY_DETAIL_HAS_RTTI + +#endif // ABSL_HAVE_STD_ANY + +#endif // ABSL_TYPES_ANY_H_ diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc new file mode 100644 index 00000000..ab04bf5a --- /dev/null +++ b/absl/types/any_test.cc @@ -0,0 +1,713 @@ +// 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. + +#include "absl/types/any.h" + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/exception_testing.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/container/internal/test_instance_tracker.h" + +namespace { +using absl::test_internal::CopyableOnlyInstance; +using absl::test_internal::InstanceTracker; + +template +const T& AsConst(const T& t) { + return t; +} + +struct MoveOnly { + MoveOnly() = default; + explicit MoveOnly(int value) : value(value) {} + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + + int value = 0; +}; + +struct CopyOnly { + CopyOnly() = default; + explicit CopyOnly(int value) : value(value) {} + CopyOnly(CopyOnly&&) = delete; + CopyOnly& operator=(CopyOnly&&) = delete; + CopyOnly(const CopyOnly&) = default; + CopyOnly& operator=(const CopyOnly&) = default; + + int value = 0; +}; + +struct MoveOnlyWithListConstructor { + MoveOnlyWithListConstructor() = default; + explicit MoveOnlyWithListConstructor(std::initializer_list /*ilist*/, + int value) + : value(value) {} + MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default; + MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) = + default; + + int value = 0; +}; + +struct IntMoveOnlyCopyOnly { + IntMoveOnlyCopyOnly(int value, MoveOnly /*move_only*/, CopyOnly /*copy_only*/) + : value(value) {} + + int value; +}; + +struct ListMoveOnlyCopyOnly { + ListMoveOnlyCopyOnly(std::initializer_list ilist, MoveOnly /*move_only*/, + CopyOnly /*copy_only*/) + : values(ilist) {} + + std::vector values; +}; + +using FunctionType = void(); +void FunctionToEmplace() {} + +using ArrayType = int[2]; +using DecayedArray = absl::decay_t; + +TEST(AnyTest, Noexcept) { + static_assert(std::is_nothrow_default_constructible(), ""); + static_assert(std::is_nothrow_move_constructible(), ""); + static_assert(std::is_nothrow_move_assignable(), ""); + static_assert(noexcept(std::declval().has_value()), ""); + static_assert(noexcept(std::declval().type()), ""); + static_assert(noexcept(absl::any_cast(std::declval())), ""); + static_assert( + noexcept(std::declval().swap(std::declval())), + ""); + + using std::swap; + static_assert( + noexcept(swap(std::declval(), std::declval())), + ""); +} + +TEST(AnyTest, HasValue) { + absl::any o; + EXPECT_FALSE(o.has_value()); + o.emplace(); + EXPECT_TRUE(o.has_value()); + o.reset(); + EXPECT_FALSE(o.has_value()); +} + +TEST(AnyTest, Type) { + absl::any o; + EXPECT_EQ(typeid(void), o.type()); + o.emplace(5); + EXPECT_EQ(typeid(int), o.type()); + o.emplace(5.f); + EXPECT_EQ(typeid(float), o.type()); + o.reset(); + EXPECT_EQ(typeid(void), o.type()); +} + +TEST(AnyTest, EmptyPointerCast) { + // pointer-to-unqualified overload + { + absl::any o; + EXPECT_EQ(nullptr, absl::any_cast(&o)); + o.emplace(); + EXPECT_NE(nullptr, absl::any_cast(&o)); + o.reset(); + EXPECT_EQ(nullptr, absl::any_cast(&o)); + } + + // pointer-to-const overload + { + absl::any o; + EXPECT_EQ(nullptr, absl::any_cast(&AsConst(o))); + o.emplace(); + EXPECT_NE(nullptr, absl::any_cast(&AsConst(o))); + o.reset(); + EXPECT_EQ(nullptr, absl::any_cast(&AsConst(o))); + } +} + +TEST(AnyTest, InPlaceConstruction) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type_t(), 5, MoveOnly(), + copy_only); + IntMoveOnlyCopyOnly& v = absl::any_cast(o); + EXPECT_EQ(5, v.value); +} + +TEST(AnyTest, InPlaceConstructionWithCV) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type_t(), 5, + MoveOnly(), copy_only); + IntMoveOnlyCopyOnly& v = absl::any_cast(o); + EXPECT_EQ(5, v.value); +} + +TEST(AnyTest, InPlaceConstructionWithFunction) { + absl::any o(absl::in_place_type_t(), FunctionToEmplace); + FunctionType*& construction_result = absl::any_cast(o); + EXPECT_EQ(&FunctionToEmplace, construction_result); +} + +TEST(AnyTest, InPlaceConstructionWithArray) { + ArrayType ar = {5, 42}; + absl::any o(absl::in_place_type_t(), ar); + DecayedArray& construction_result = absl::any_cast(o); + EXPECT_EQ(&ar[0], construction_result); +} + +TEST(AnyTest, InPlaceConstructionIlist) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type_t(), {1, 2, 3, 4}, + MoveOnly(), copy_only); + ListMoveOnlyCopyOnly& v = absl::any_cast(o); + std::vector expected_values = {1, 2, 3, 4}; + EXPECT_EQ(expected_values, v.values); +} + +TEST(AnyTest, InPlaceConstructionIlistWithCV) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type_t(), + {1, 2, 3, 4}, MoveOnly(), copy_only); + ListMoveOnlyCopyOnly& v = absl::any_cast(o); + std::vector expected_values = {1, 2, 3, 4}; + EXPECT_EQ(expected_values, v.values); +} + +TEST(AnyTest, InPlaceNoArgs) { + absl::any o(absl::in_place_type_t{}); + EXPECT_EQ(0, absl::any_cast(o)); +} + +template +struct CanEmplaceAnyImpl : std::false_type {}; + +template +struct CanEmplaceAnyImpl< + absl::void_t().emplace(std::declval()...))>, + T, Args...> : std::true_type {}; + +template +using CanEmplaceAny = CanEmplaceAnyImpl; + +TEST(AnyTest, Emplace) { + const CopyOnly copy_only{}; + absl::any o; + EXPECT_TRUE((std::is_same( + 5, MoveOnly(), copy_only)), + IntMoveOnlyCopyOnly&>::value)); + IntMoveOnlyCopyOnly& emplace_result = + o.emplace(5, MoveOnly(), copy_only); + EXPECT_EQ(5, emplace_result.value); + IntMoveOnlyCopyOnly& v = absl::any_cast(o); + EXPECT_EQ(5, v.value); + EXPECT_EQ(&emplace_result, &v); + + static_assert(!CanEmplaceAny::value, ""); + static_assert(!CanEmplaceAny::value, ""); +} + +TEST(AnyTest, EmplaceWithCV) { + const CopyOnly copy_only{}; + absl::any o; + EXPECT_TRUE( + (std::is_same( + 5, MoveOnly(), copy_only)), + IntMoveOnlyCopyOnly&>::value)); + IntMoveOnlyCopyOnly& emplace_result = + o.emplace(5, MoveOnly(), copy_only); + EXPECT_EQ(5, emplace_result.value); + IntMoveOnlyCopyOnly& v = absl::any_cast(o); + EXPECT_EQ(5, v.value); + EXPECT_EQ(&emplace_result, &v); +} + +TEST(AnyTest, EmplaceWithFunction) { + absl::any o; + EXPECT_TRUE( + (std::is_same(FunctionToEmplace)), + FunctionType*&>::value)); + FunctionType*& emplace_result = o.emplace(FunctionToEmplace); + EXPECT_EQ(&FunctionToEmplace, emplace_result); +} + +TEST(AnyTest, EmplaceWithArray) { + absl::any o; + ArrayType ar = {5, 42}; + EXPECT_TRUE( + (std::is_same(ar)), DecayedArray&>::value)); + DecayedArray& emplace_result = o.emplace(ar); + EXPECT_EQ(&ar[0], emplace_result); +} + +TEST(AnyTest, EmplaceIlist) { + const CopyOnly copy_only{}; + absl::any o; + EXPECT_TRUE((std::is_same( + {1, 2, 3, 4}, MoveOnly(), copy_only)), + ListMoveOnlyCopyOnly&>::value)); + ListMoveOnlyCopyOnly& emplace_result = + o.emplace({1, 2, 3, 4}, MoveOnly(), copy_only); + ListMoveOnlyCopyOnly& v = absl::any_cast(o); + EXPECT_EQ(&v, &emplace_result); + std::vector expected_values = {1, 2, 3, 4}; + EXPECT_EQ(expected_values, v.values); + + static_assert(!CanEmplaceAny>::value, ""); + static_assert(!CanEmplaceAny, int>::value, + ""); +} + +TEST(AnyTest, EmplaceIlistWithCV) { + const CopyOnly copy_only{}; + absl::any o; + EXPECT_TRUE( + (std::is_same( + {1, 2, 3, 4}, MoveOnly(), copy_only)), + ListMoveOnlyCopyOnly&>::value)); + ListMoveOnlyCopyOnly& emplace_result = + o.emplace({1, 2, 3, 4}, MoveOnly(), + copy_only); + ListMoveOnlyCopyOnly& v = absl::any_cast(o); + EXPECT_EQ(&v, &emplace_result); + std::vector expected_values = {1, 2, 3, 4}; + EXPECT_EQ(expected_values, v.values); +} + +TEST(AnyTest, EmplaceNoArgs) { + absl::any o; + o.emplace(); + EXPECT_EQ(0, absl::any_cast(o)); +} + +TEST(AnyTest, ConversionConstruction) { + { + absl::any o = 5; + EXPECT_EQ(5, absl::any_cast(o)); + } + + { + const CopyOnly copy_only(5); + absl::any o = copy_only; + EXPECT_EQ(5, absl::any_cast(o).value); + } + + static_assert(!std::is_convertible::value, ""); +} + +TEST(AnyTest, ConversionAssignment) { + { + absl::any o; + o = 5; + EXPECT_EQ(5, absl::any_cast(o)); + } + + { + const CopyOnly copy_only(5); + absl::any o; + o = copy_only; + EXPECT_EQ(5, absl::any_cast(o).value); + } + + static_assert(!std::is_assignable::value, ""); +} + +// Suppress MSVC warnings. +// 4521: multiple copy constructors specified +// We wrote multiple of them to test that the correct overloads are selected. +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4521) +#endif + +// Weird type for testing, only used to make sure we "properly" perfect-forward +// when being placed into an absl::any (use the l-value constructor if given an +// l-value rather than use the copy constructor). +struct WeirdConstructor42 { + explicit WeirdConstructor42(int value) : value(value) {} + + // Copy-constructor + WeirdConstructor42(const WeirdConstructor42& other) : value(other.value) {} + + // L-value "weird" constructor (used when given an l-value) + WeirdConstructor42( + WeirdConstructor42& /*other*/) // NOLINT(runtime/references) + : value(42) {} + + int value; +}; +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +TEST(AnyTest, WeirdConversionConstruction) { + { + const WeirdConstructor42 source(5); + absl::any o = source; // Actual copy + EXPECT_EQ(5, absl::any_cast(o).value); + } + + { + WeirdConstructor42 source(5); + absl::any o = source; // Weird "conversion" + EXPECT_EQ(42, absl::any_cast(o).value); + } +} + +TEST(AnyTest, WeirdConversionAssignment) { + { + const WeirdConstructor42 source(5); + absl::any o; + o = source; // Actual copy + EXPECT_EQ(5, absl::any_cast(o).value); + } + + { + WeirdConstructor42 source(5); + absl::any o; + o = source; // Weird "conversion" + EXPECT_EQ(42, absl::any_cast(o).value); + } +} + +struct Value {}; + +TEST(AnyTest, AnyCastValue) { + { + absl::any o; + o.emplace(5); + EXPECT_EQ(5, absl::any_cast(o)); + EXPECT_EQ(5, absl::any_cast(AsConst(o))); + static_assert( + std::is_same(o)), Value>::value, ""); + } + + { + absl::any o; + o.emplace(5); + EXPECT_EQ(5, absl::any_cast(o)); + EXPECT_EQ(5, absl::any_cast(AsConst(o))); + static_assert(std::is_same(o)), + const Value>::value, + ""); + } +} + +TEST(AnyTest, AnyCastReference) { + { + absl::any o; + o.emplace(5); + EXPECT_EQ(5, absl::any_cast(o)); + EXPECT_EQ(5, absl::any_cast(AsConst(o))); + static_assert( + std::is_same(o)), Value&>::value, ""); + } + + { + absl::any o; + o.emplace(5); + EXPECT_EQ(5, absl::any_cast(o)); + EXPECT_EQ(5, absl::any_cast(AsConst(o))); + static_assert(std::is_same(o)), + const Value&>::value, + ""); + } + + { + absl::any o; + o.emplace(5); + EXPECT_EQ(5, absl::any_cast(std::move(o))); + static_assert(std::is_same(std::move(o))), + Value&&>::value, + ""); + } + + { + absl::any o; + o.emplace(5); + EXPECT_EQ(5, absl::any_cast(std::move(o))); + static_assert( + std::is_same(std::move(o))), + const Value&&>::value, + ""); + } +} + +TEST(AnyTest, AnyCastPointer) { + { + absl::any o; + EXPECT_EQ(nullptr, absl::any_cast(&o)); + o.emplace(5); + EXPECT_EQ(nullptr, absl::any_cast(&o)); + o.emplace('a'); + EXPECT_EQ('a', *absl::any_cast(&o)); + static_assert( + std::is_same(&o)), Value*>::value, ""); + } + + { + absl::any o; + EXPECT_EQ(nullptr, absl::any_cast(&o)); + o.emplace(5); + EXPECT_EQ(nullptr, absl::any_cast(&o)); + o.emplace('a'); + EXPECT_EQ('a', *absl::any_cast(&o)); + static_assert(std::is_same(&o)), + const Value*>::value, + ""); + } +} + +TEST(AnyTest, MakeAny) { + const CopyOnly copy_only{}; + auto o = absl::make_any(5, MoveOnly(), copy_only); + static_assert(std::is_same::value, ""); + EXPECT_EQ(5, absl::any_cast(o).value); +} + +TEST(AnyTest, MakeAnyIList) { + const CopyOnly copy_only{}; + auto o = + absl::make_any({1, 2, 3}, MoveOnly(), copy_only); + static_assert(std::is_same::value, ""); + ListMoveOnlyCopyOnly& v = absl::any_cast(o); + std::vector expected_values = {1, 2, 3}; + EXPECT_EQ(expected_values, v.values); +} + +// Test the use of copy constructor and operator= +TEST(AnyTest, Copy) { + InstanceTracker tracker_raii; + + { + absl::any o(absl::in_place_type_t{}, 123); + CopyableOnlyInstance* f1 = absl::any_cast(&o); + + absl::any o2(o); + const CopyableOnlyInstance* f2 = absl::any_cast(&o2); + EXPECT_EQ(123, f2->value()); + EXPECT_NE(f1, f2); + + absl::any o3; + o3 = o2; + const CopyableOnlyInstance* f3 = absl::any_cast(&o3); + EXPECT_EQ(123, f3->value()); + EXPECT_NE(f2, f3); + + const absl::any o4(4); + // copy construct from const lvalue ref. + absl::any o5 = o4; + EXPECT_EQ(4, absl::any_cast(o4)); + EXPECT_EQ(4, absl::any_cast(o5)); + + // Copy construct from const rvalue ref. + absl::any o6 = std::move(o4); // NOLINT + EXPECT_EQ(4, absl::any_cast(o4)); + EXPECT_EQ(4, absl::any_cast(o6)); + } +} + +TEST(AnyTest, Move) { + InstanceTracker tracker_raii; + + absl::any any1; + any1.emplace(5); + + // This is a copy, so copy count increases to 1. + absl::any any2 = any1; + EXPECT_EQ(5, absl::any_cast(any1).value()); + EXPECT_EQ(5, absl::any_cast(any2).value()); + EXPECT_EQ(1, tracker_raii.copies()); + + // This isn't a copy, so copy count doesn't increase. + absl::any any3 = std::move(any2); + EXPECT_EQ(5, absl::any_cast(any3).value()); + EXPECT_EQ(1, tracker_raii.copies()); + + absl::any any4; + any4 = std::move(any3); + EXPECT_EQ(5, absl::any_cast(any4).value()); + EXPECT_EQ(1, tracker_raii.copies()); + + absl::any tmp4(4); + absl::any o4(std::move(tmp4)); // move construct + EXPECT_EQ(4, absl::any_cast(o4)); + o4 = o4; // self assign + EXPECT_EQ(4, absl::any_cast(o4)); + EXPECT_TRUE(o4.has_value()); + + absl::any o5; + absl::any tmp5(5); + o5 = std::move(tmp5); // move assign + EXPECT_EQ(5, absl::any_cast(o5)); +} + +// Reset the ObjectOwner with an object of a different type +TEST(AnyTest, Reset) { + absl::any o; + o.emplace(); + + o.reset(); + EXPECT_FALSE(o.has_value()); + + o.emplace(); + EXPECT_TRUE(o.has_value()); +} + +TEST(AnyTest, ConversionConstructionCausesOneCopy) { + InstanceTracker tracker_raii; + CopyableOnlyInstance counter(5); + absl::any o(counter); + EXPECT_EQ(5, absl::any_cast(o).value()); + EXPECT_EQ(1, tracker_raii.copies()); +} + +////////////////////////////////// +// Tests for Exception Behavior // +////////////////////////////////// + +#define ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(...) \ + ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), absl::bad_any_cast, \ + "Bad any cast") + +TEST(AnyTest, ThrowBadAlloc) { + { + absl::any a; + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + + // const absl::any operand + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(AsConst(a))); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(AsConst(a))); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(AsConst(a))); + } + + { + absl::any a(absl::in_place_type_t{}); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST( + absl::any_cast(absl::any{})); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(a)); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(absl::any{})); + + // const absl::any operand + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(AsConst(a))); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(AsConst(a))); + ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast(AsConst(a))); + } +} + +class BadCopy {}; + +struct BadCopyable { + BadCopyable() = default; + BadCopyable(BadCopyable&&) = default; + BadCopyable(const BadCopyable&) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw BadCopy(); +#else + ABSL_RAW_LOG(FATAL, "Bad copy"); +#endif + } +}; + +#define ABSL_ANY_TEST_EXPECT_BAD_COPY(...) \ + ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), BadCopy, "Bad copy") + +// Test the guarantees regarding exceptions in copy/assign. +TEST(AnyTest, FailedCopy) { + { + const BadCopyable bad{}; + ABSL_ANY_TEST_EXPECT_BAD_COPY(absl::any{bad}); + } + + { + absl::any src(absl::in_place_type_t{}); + ABSL_ANY_TEST_EXPECT_BAD_COPY(absl::any{src}); + } + + { + BadCopyable bad; + absl::any target; + ABSL_ANY_TEST_EXPECT_BAD_COPY(target = bad); + } + + { + BadCopyable bad; + absl::any target(absl::in_place_type_t{}); + ABSL_ANY_TEST_EXPECT_BAD_COPY(target = bad); + EXPECT_TRUE(target.has_value()); + } + + { + absl::any src(absl::in_place_type_t{}); + absl::any target; + ABSL_ANY_TEST_EXPECT_BAD_COPY(target = src); + EXPECT_FALSE(target.has_value()); + } + + { + absl::any src(absl::in_place_type_t{}); + absl::any target(absl::in_place_type_t{}); + ABSL_ANY_TEST_EXPECT_BAD_COPY(target = src); + EXPECT_TRUE(target.has_value()); + } +} + +// Test the guarantees regarding exceptions in emplace. +TEST(AnyTest, FailedEmplace) { + { + BadCopyable bad; + absl::any target; + ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); + } + + { + BadCopyable bad; + absl::any target(absl::in_place_type_t{}); + ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); +#if defined(ABSL_HAVE_STD_ANY) && defined(__GLIBCXX__) + // libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an + // exception is thrown, *this contains a value. +#define ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG 1 +#endif +#if defined(ABSL_HAVE_EXCEPTIONS) && \ + !defined(ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG) + EXPECT_FALSE(target.has_value()); +#endif + } +} + +} // namespace diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc new file mode 100644 index 00000000..c9b73300 --- /dev/null +++ b/absl/types/bad_any_cast.cc @@ -0,0 +1,40 @@ +// 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. + +#include "absl/types/bad_any_cast.h" + +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { + +bad_any_cast::~bad_any_cast() = default; + +const char* bad_any_cast::what() const noexcept { return "Bad any cast"; } + +namespace any_internal { + +void ThrowBadAnyCast() { +#ifdef ABSL_HAVE_EXCEPTIONS + throw bad_any_cast(); +#else + ABSL_RAW_LOG(FATAL, "Bad any cast"); + std::abort(); +#endif +} + +} // namespace any_internal +} // namespace absl diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h new file mode 100644 index 00000000..8ffbe4bf --- /dev/null +++ b/absl/types/bad_any_cast.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef ABSL_TYPES_BAD_ANY_CAST_H_ +#define ABSL_TYPES_BAD_ANY_CAST_H_ + +#include + +namespace absl { + +//////////////////////// +// [any.bad_any_cast] // +//////////////////////// + +// Objects of type bad_any_cast are thrown by a failed any_cast. +class bad_any_cast : public std::bad_cast { + public: + ~bad_any_cast() override; + const char* what() const noexcept override; +}; + +////////////////////////////////////////////// +// Implementation-details beyond this point // +////////////////////////////////////////////// + +namespace any_internal { + +[[noreturn]] void ThrowBadAnyCast(); + +} // namespace any_internal +} // namespace absl + +#endif // ABSL_TYPES_BAD_ANY_CAST_H_ diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc new file mode 100644 index 00000000..6bc67df7 --- /dev/null +++ b/absl/types/bad_optional_access.cc @@ -0,0 +1,42 @@ +// 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. + +#include "absl/types/bad_optional_access.h" + +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { + +bad_optional_access::~bad_optional_access() = default; + +const char* bad_optional_access::what() const noexcept { + return "optional has no value"; +} + +namespace optional_internal { + +void throw_bad_optional_access() { +#ifdef ABSL_HAVE_EXCEPTIONS + throw bad_optional_access(); +#else + ABSL_RAW_LOG(FATAL, "Bad optional access"); + abort(); +#endif +} + +} // namespace optional_internal +} // namespace absl diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h new file mode 100644 index 00000000..c4c74447 --- /dev/null +++ b/absl/types/bad_optional_access.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ +#define ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ + +#include + +namespace absl { + +class bad_optional_access : public std::exception { + public: + bad_optional_access() = default; + ~bad_optional_access() override; + const char* what() const noexcept override; +}; + +namespace optional_internal { + +// throw delegator +[[noreturn]] void throw_bad_optional_access(); + +} // namespace optional_internal +} // namespace absl + +#endif // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ diff --git a/absl/types/optional.cc b/absl/types/optional.cc new file mode 100644 index 00000000..ef272904 --- /dev/null +++ b/absl/types/optional.cc @@ -0,0 +1,24 @@ +// 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. + +#include "absl/types/optional.h" + +#ifndef ABSL_HAVE_STD_OPTIONAL +namespace absl { + +nullopt_t::init_t nullopt_t::init; +extern const nullopt_t nullopt{nullopt_t::init}; + +} // namespace absl +#endif // ABSL_HAVE_STD_OPTIONAL diff --git a/absl/types/optional.h b/absl/types/optional.h new file mode 100644 index 00000000..5099d489 --- /dev/null +++ b/absl/types/optional.h @@ -0,0 +1,1092 @@ +// +// 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. +// +// ----------------------------------------------------------------------------- +// optional.h +// ----------------------------------------------------------------------------- +// +// This header file define the `absl::optional` type for holding a value which +// may or may not be present. This type is useful for providing value semantics +// for operations that may either wish to return or hold "something-or-nothing". +// +// Example: +// +// // A common way to signal operation failure is to provide an output +// // parameter and a bool return type: +// bool AcquireResource(const Input&, Resource * out); +// +// // Providing an absl::optional return type provides a cleaner API: +// absl::optional AcquireResource(const Input&); +// +// `absl::optional` is a C++11 compatible version of the C++17 `std::optional` +// abstraction and is designed to be a drop-in replacement for code compliant +// with C++17. +#ifndef ABSL_TYPES_OPTIONAL_H_ +#define ABSL_TYPES_OPTIONAL_H_ + +#include "absl/base/config.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_STD_OPTIONAL + +#include + +namespace absl { +using std::bad_optional_access; +using std::optional; +using std::make_optional; +using std::nullopt_t; +using std::nullopt; +} + +#else // ABSL_HAVE_STD_OPTIONAL + +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/types/bad_optional_access.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 +// 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 { + +// optional +// +// A value of type `absl::optional` holds either a value of `T` or an +// "empty" value. When it holds a value of `T`, it stores it as a direct +// sub-object, so `sizeof(optional)` is approximately +// `sizeof(T) + sizeof(bool)`. +// +// This implementation is based on the specification in the latest draft of the +// C++17 `std::optional` specification as of May 2017, section 20.6. +// +// Differences between `absl::optional` and `std::optional` include: +// +// * `constexpr` is not used for non-const member functions. +// (dependency on some differences between C++11 and C++14.) +// * `absl::nullopt` and `absl::in_place` are not declared `constexpr`. We +// need the inline variable support in C++17 for external linkage. +// * Throws `absl::bad_optional_access` instead of +// `std::bad_optional_access`. +// * `optional::swap()` and `absl::swap()` relies on +// `std::is_(nothrow_)swappable()`, which has been introduced in C++17. +// As a workaround, we assume `is_swappable()` is always `true` +// and `is_nothrow_swappable()` is the same as `std::is_trivial()`. +// * `make_optional()` cannot be declared `constexpr` due to the absence of +// guaranteed copy elision. +template +class optional; + +// nullopt_t +// +// Class type for `absl::nullopt` used to indicate an `absl::optional` type +// that does not contain a value. +struct nullopt_t { + struct init_t {}; + static init_t init; + + // It must not be default-constructible to avoid ambiguity for opt = {}. + // Note the non-const reference, which is to eliminate ambiguity for code + // like: + // + // struct S { int value; }; + // + // void Test() { + // optional opt; + // opt = {{}}; + // } + explicit constexpr nullopt_t(init_t& /*unused*/) {} +}; + +// nullopt +// +// A tag constant of type `absl::nullopt_t` used to indicate an empty +// `absl::optional` in certain functions, such as construction or assignment. +extern const nullopt_t nullopt; + +namespace optional_internal { + +struct empty_struct {}; +// This class stores the data in optional. +// It is specialized based on whether T is trivially destructible. +// This is the specialization for non trivially destructible type. +template ::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 { + dummy_type dummy_; + T data_; + }; + + void destruct() noexcept { + if (engaged_) { + data_.~T(); + engaged_ = false; + } + } + + // dummy_ must be initialized for constexpr constructor. + constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} + + template + constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) + : engaged_(true), data_(absl::forward(args)...) {} + + ~optional_data_dtor_base() { destruct(); } +}; + +// Specialization for trivially destructible type. +template +class optional_data_dtor_base { + 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 { + dummy_type dummy_; + T data_; + }; + void destruct() noexcept { engaged_ = false; } + + // dummy_ must be initialized for constexpr constructor. + constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} + + template + constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) + : engaged_(true), data_(absl::forward(args)...) {} +}; + +template +class optional_data_base : public optional_data_dtor_base { + protected: + using base = optional_data_dtor_base; +#if ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using base::base; +#else + optional_data_base() = default; + + template + constexpr explicit optional_data_base(in_place_t t, Args&&... args) + : base(t, absl::forward(args)...) {} +#endif + + template + void construct(Args&&... args) { + // Use dummy_'s address to work around casting cv-qualified T* to void*. + ::new (static_cast(&this->dummy_)) T(std::forward(args)...); + this->engaged_ = true; + } + + template + void assign(U&& u) { + if (this->engaged_) { + this->data_ = std::forward(u); + } else { + construct(std::forward(u)); + } + } +}; + +// TODO(b/34201852): Add another base 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 ::value&& + absl::is_trivially_copy_assignable< + typename std::remove_cv::type>::value&& + std::is_trivially_destructible::value> +class optional_data; + +// Trivially copyable types +template +class optional_data : public optional_data_base { + protected: +#if ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using optional_data_base::optional_data_base; +#else + optional_data() = default; + + template + constexpr explicit optional_data(in_place_t t, Args&&... args) + : optional_data_base(t, absl::forward(args)...) {} +#endif +}; + +template +class optional_data : public optional_data_base { + protected: +#if ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using optional_data_base::optional_data_base; +#else + template + constexpr explicit optional_data(in_place_t t, Args&&... args) + : optional_data_base(t, absl::forward(args)...) {} +#endif + + optional_data() = default; + + optional_data(const optional_data& rhs) { + if (rhs.engaged_) { + this->construct(rhs.data_); + } + } + + optional_data(optional_data&& rhs) noexcept( + absl::default_allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value) { + 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::value&& + std::is_nothrow_move_constructible::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 +class optional_ctor_base; + +template <> +class optional_ctor_base { + 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 { + 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 { + 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 +class optional_assign_base; + +template <> +class optional_assign_base { + 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 { + 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 { + 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 +constexpr copy_traits get_ctor_copy_traits() { + return std::is_copy_constructible::value + ? copy_traits::copyable + : std::is_move_constructible::value ? copy_traits::movable + : copy_traits::non_movable; +} + +template +constexpr copy_traits get_assign_copy_traits() { + return std::is_copy_assignable::value && + std::is_copy_constructible::value + ? copy_traits::copyable + : std::is_move_assignable::value && + std::is_move_constructible::value + ? copy_traits::movable + : copy_traits::non_movable; +} + +// Whether T is constructible or convertible from optional. +template +struct is_constructible_convertible_from_optional + : std::integral_constant< + bool, std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_convertible&, T>::value || + std::is_convertible&&, T>::value || + std::is_convertible&, T>::value || + std::is_convertible&&, T>::value> {}; + +// Whether T is constructible or convertible or assignable from optional. +template +struct is_constructible_convertible_assignable_from_optional + : std::integral_constant< + bool, is_constructible_convertible_from_optional::value || + std::is_assignable&>::value || + std::is_assignable&&>::value || + std::is_assignable&>::value || + std::is_assignable&&>::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); + +} // namespace optional_internal + +// ----------------------------------------------------------------------------- +// absl::optional class definition +// ----------------------------------------------------------------------------- + +template +class optional : private optional_internal::optional_data, + private optional_internal::optional_ctor_base< + optional_internal::get_ctor_copy_traits()>, + private optional_internal::optional_assign_base< + optional_internal::get_assign_copy_traits()> { + using data_base = optional_internal::optional_data; + + public: + typedef T value_type; + + // Constructors + + // Constructs a default-constructed `optional` holding the empty value, NOT a + // default constructed `T`. + constexpr optional() noexcept {} + + // Construct an` optional` initialized with `nullopt` to hold an empty value. + constexpr optional(nullopt_t) noexcept {} // NOLINT(runtime/explicit) + + // Copy constructor, standard semantics + optional(const optional& src) = default; + + // Move constructor, standard semantics + optional(optional&& src) = default; + + // Constructs a non-empty `optional` direct-initialized value of type `T` from + // the arguments `std::forward(args)...` within the `optional`. + // (The `in_place_t` is a tag used to indicate that the contained object + // should be constructed in-place.) + // TODO(b/34201852): Add std::is_constructible SFINAE. + template + constexpr explicit optional(in_place_t, Args&&... args) + : data_base(in_place_t(), absl::forward(args)...) {} + + // Constructs a non-empty `optional' direct-initialized value of type `T` from + // the arguments of an initializer_list and `std::forward(args)...`. + // (The `in_place_t` is a tag used to indicate that the contained object + // should be constructed in-place.) + template &, Args&&...>::value>::type> + constexpr explicit optional(in_place_t, std::initializer_list il, + Args&&... args) + : data_base(in_place_t(), il, absl::forward(args)...) { + } + + // Value constructor (implicit) + template < + typename U = T, + typename std::enable_if< + absl::conjunction::type> >, + absl::negation, typename std::decay::type> >, + std::is_convertible, + std::is_constructible >::value, + bool>::type = false> + constexpr optional(U&& v) : data_base(in_place_t(), absl::forward(v)) {} + + // Value constructor (explicit) + template < + typename U = T, + typename std::enable_if< + absl::conjunction::type>>, + absl::negation, typename std::decay::type>>, + absl::negation>, + std::is_constructible>::value, + bool>::type = false> + explicit constexpr optional(U&& v) + : data_base(in_place_t(), absl::forward(v)) {} + + // Converting copy constructor (implicit) + template >, + std::is_constructible, + absl::negation< + optional_internal:: + is_constructible_convertible_from_optional >, + std::is_convertible >::value, + bool>::type = false> + optional(const optional& rhs) { + if (rhs) { + this->construct(*rhs); + } + } + + // Converting copy constructor (explicit) + template >, + std::is_constructible, + absl::negation< + optional_internal:: + is_constructible_convertible_from_optional>, + absl::negation>>::value, + bool>::type = false> + explicit optional(const optional& rhs) { + if (rhs) { + this->construct(*rhs); + } + } + + // Converting move constructor (implicit) + template >, + std::is_constructible, + absl::negation< + optional_internal:: + is_constructible_convertible_from_optional >, + std::is_convertible >::value, + bool>::type = false> + optional(optional&& rhs) { + if (rhs) { + this->construct(std::move(*rhs)); + } + } + + // Converting move constructor (explicit) + template < + typename U, + typename std::enable_if< + absl::conjunction< + absl::negation>, std::is_constructible, + absl::negation< + optional_internal::is_constructible_convertible_from_optional< + T, U>>, + absl::negation>>::value, + bool>::type = false> + explicit optional(optional&& rhs) { + if (rhs) { + this->construct(std::move(*rhs)); + } + } + + // Destructor. Trivial if `T` is trivially destructible. + ~optional() = default; + + // Assignment Operators + + // Assignment from `nullopt` + // + // Example: + // + // struct S { int value; }; + // optional opt = absl::nullopt; // Could also use opt = { }; + optional& operator=(nullopt_t) noexcept { + this->destruct(); + return *this; + } + + // Copy assignment operator, standard semantics + optional& operator=(const optional& src) = default; + + // Move assignment operator, standard semantics + optional& operator=(optional&& src) = default; + + // Value assignment operators + template < + typename U = T, + typename = typename std::enable_if, typename std::decay::type>>, + absl::negation< + absl::conjunction, + std::is_same::type>>>, + std::is_constructible, std::is_assignable>::value>::type> + optional& operator=(U&& v) { + this->assign(std::forward(v)); + return *this; + } + + template < + typename U, + typename = typename std::enable_if>, + std::is_constructible, std::is_assignable, + absl::negation< + optional_internal:: + is_constructible_convertible_assignable_from_optional< + T, U>>>::value>::type> + optional& operator=(const optional& rhs) { + if (rhs) { + this->assign(*rhs); + } else { + this->destruct(); + } + return *this; + } + + template >, std::is_constructible, + std::is_assignable, + absl::negation< + optional_internal:: + is_constructible_convertible_assignable_from_optional< + T, U>>>::value>::type> + optional& operator=(optional&& rhs) { + if (rhs) { + this->assign(std::move(*rhs)); + } else { + this->destruct(); + } + return *this; + } + + // Modifiers + + // optional::reset() + // + // Destroys the inner `T` value of an `absl::optional` if one is present. + void reset() noexcept { this->destruct(); } + + // optional::emplace() + // + // (Re)constructs the underlying `T` in-place with the given forwarded + // arguments. + // + // Example: + // + // optional opt; + // opt.emplace(arg1,arg2,arg3); // Constructs Foo(arg1,arg2,arg3) + // + // If the optional is non-empty, and the `args` refer to subobjects of the + // current object, then behaviour is undefined, because the current object + // will be destructed before the new object is constructed with `args`. + template ::value>::type> + T& emplace(Args&&... args) { + this->destruct(); + this->construct(std::forward(args)...); + return reference(); + } + + // Emplace reconstruction overload for an initializer list and the given + // forwarded arguments. + // + // Example: + // + // struct Foo { + // Foo(std::initializer_list); + // }; + // + // optional opt; + // opt.emplace({1,2,3}); // Constructs Foo({1,2,3}) + template &, Args&&...>::value>::type> + T& emplace(std::initializer_list il, Args&&... args) { + this->destruct(); + this->construct(il, std::forward(args)...); + return reference(); + } + + // Swaps + + // Swap, standard semantics + void swap(optional& rhs) noexcept( + std::is_nothrow_move_constructible::value&& + std::is_trivial::value) { + if (*this) { + if (rhs) { + using std::swap; + swap(**this, *rhs); + } else { + rhs.construct(std::move(**this)); + this->destruct(); + } + } else { + if (rhs) { + this->construct(std::move(*rhs)); + rhs.destruct(); + } else { + // No effect (swap(disengaged, disengaged)). + } + } + } + + // Observers + + // optional::operator->() + // + // Accesses the underlying `T` value's member `m` of an `optional`. If the + // `optional` is empty, behavior is undefined. + constexpr const T* operator->() const { return this->pointer(); } + T* operator->() { + assert(this->engaged_); + return this->pointer(); + } + + // optional::operator*() + // + // Accesses the underlying `T `value of an `optional`. If the `optional` is + // empty, behavior is undefined. + constexpr const T& operator*() const & { return reference(); } + T& operator*() & { + assert(this->engaged_); + return reference(); + } + constexpr const T&& operator*() const && { + return absl::move(reference()); + } + T&& operator*() && { + assert(this->engaged_); + return std::move(reference()); + } + + // optional::operator bool() + // + // Returns false if and only if the `optional` is empty. + // + // if (opt) { + // // do something with opt.value(); + // } else { + // // opt is empty. + // } + // + constexpr explicit operator bool() const noexcept { return this->engaged_; } + + // optional::has_value() + // + // Determines whether the `optional` contains a value. Returns `false` if and + // only if `*this` is empty. + constexpr bool has_value() const noexcept { return this->engaged_; } + + // optional::value() + // + // Returns a reference to an `optional`s underlying value. The constness + // and lvalue/rvalue-ness of the `optional` is preserved to the view of + // the `T` sub-object. Throws `absl::bad_optional_access` when the `optional` + // is empty. + constexpr const T& value() const & { + return static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference()); + } + T& value() & { + return static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference()); + } + T&& value() && { // NOLINT(build/c++11) + return std::move( + static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference())); + } + constexpr const T&& value() const && { // NOLINT(build/c++11) + return absl::move( + static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference())); + } + + // optional::value_or() + // + // Returns either the value of `T` or a passed default `val` if the `optional` + // is empty. + template + constexpr T value_or(U&& v) const& { + return static_cast(*this) + ? **this + : static_cast(absl::forward(v)); + } + template + T value_or(U&& v) && { // NOLINT(build/c++11) + return static_cast(*this) ? std::move(**this) + : static_cast(std::forward(v)); + } + + private: + // Private accessors for internal storage viewed as pointer to T. + constexpr const T* pointer() const { return &this->data_; } + T* pointer() { return &this->data_; } + + // Private accessors for internal storage viewed as reference to T. + constexpr const T& reference() const { return *this->pointer(); } + T& reference() { return *(this->pointer()); } + + // T constraint checks. You can't have an optional of nullopt_t, in_place_t + // or a reference. + static_assert( + !std::is_same::type>::value, + "optional is not allowed."); + static_assert( + !std::is_same::type>::value, + "optional is not allowed."); + static_assert(!std::is_reference::value, + "optional is not allowed."); +}; + +// Non-member functions + +// swap() +// +// Performs a swap between two `absl::optional` objects, using standard +// semantics. +// +// NOTE: we assume `is_swappable()` is always `true`. A compile error will +// result if this is not the case. +template ::value, + bool>::type = false> +void swap(optional& a, optional& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +// make_optional() +// +// Creates a non-empty `optional` where the type of `T` is deduced. An +// `absl::optional` can also be explicitly instantiated with +// `make_optional(v)`. +// +// Note: `make_optional()` constructions may be declared `constexpr` for +// trivially copyable types `T`. Non-trivial types require copy elision +// support in C++17 for `make_optional` to support `constexpr` on such +// non-trivial types. +// +// Example: +// +// constexpr absl::optional opt = absl::make_optional(1); +// static_assert(opt.value() == 1, ""); +template +constexpr optional::type> make_optional(T&& v) { + return optional::type>(absl::forward(v)); +} + +template +constexpr optional make_optional(Args&&... args) { + return optional(in_place_t(), absl::forward(args)...); +} + +template +constexpr optional make_optional(std::initializer_list il, + Args&&... args) { + return optional(in_place_t(), il, + absl::forward(args)...); +} + +// Relational operators [optional.relops] + +// Empty optionals are considered equal to each other and less than non-empty +// optionals. Supports relations between optional and optional, between +// optional and U, and between optional and nullopt. +// +// Note: We're careful to support T having non-bool relationals. + +// Requires: The expression, e.g. "*x == *y" shall be well-formed and its result +// shall be convertible to bool. +// The C++17 (N4606) "Returns:" statements are translated into +// code in an obvious way here, and the original text retained as function docs. +// Returns: If bool(x) != bool(y), false; otherwise if bool(x) == false, true; +// otherwise *x == *y. +template +constexpr auto operator==(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x == *y)) { + return static_cast(x) != static_cast(y) + ? false + : static_cast(x) == false ? true : *x == *y; +} + +// Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false; +// otherwise *x != *y. +template +constexpr auto operator!=(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x != *y)) { + return static_cast(x) != static_cast(y) + ? true + : static_cast(x) == false ? false : *x != *y; +} +// Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y. +template +constexpr auto operator<(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x < *y)) { + return !y ? false : !x ? true : *x < *y; +} +// Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y. +template +constexpr auto operator>(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x > *y)) { + return !x ? false : !y ? true : *x > *y; +} +// Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y. +template +constexpr auto operator<=(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x <= *y)) { + return !x ? true : !y ? false : *x <= *y; +} +// Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y. +template +constexpr auto operator>=(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x >= *y)) { + return !y ? true : !x ? false : *x >= *y; +} + +// Comparison with nullopt [optional.nullops] +// The C++17 (N4606) "Returns:" statements are used directly here. +template +constexpr bool operator==(const optional& x, nullopt_t) noexcept { + return !x; +} +template +constexpr bool operator==(nullopt_t, const optional& x) noexcept { + return !x; +} +template +constexpr bool operator!=(const optional& x, nullopt_t) noexcept { + return static_cast(x); +} +template +constexpr bool operator!=(nullopt_t, const optional& x) noexcept { + return static_cast(x); +} +template +constexpr bool operator<(const optional&, nullopt_t) noexcept { + return false; +} +template +constexpr bool operator<(nullopt_t, const optional& x) noexcept { + return static_cast(x); +} +template +constexpr bool operator<=(const optional& x, nullopt_t) noexcept { + return !x; +} +template +constexpr bool operator<=(nullopt_t, const optional&) noexcept { + return true; +} +template +constexpr bool operator>(const optional& x, nullopt_t) noexcept { + return static_cast(x); +} +template +constexpr bool operator>(nullopt_t, const optional&) noexcept { + return false; +} +template +constexpr bool operator>=(const optional&, nullopt_t) noexcept { + return true; +} +template +constexpr bool operator>=(nullopt_t, const optional& x) noexcept { + return !x; +} + +// Comparison with T [optional.comp_with_t] + +// Requires: The expression, e.g. "*x == v" shall be well-formed and its result +// shall be convertible to bool. +// The C++17 (N4606) "Equivalent to:" statements are used directly here. +template +constexpr auto operator==(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x == v)) { + return static_cast(x) ? *x == v : false; +} +template +constexpr auto operator==(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v == *x)) { + return static_cast(x) ? v == *x : false; +} +template +constexpr auto operator!=(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x != v)) { + return static_cast(x) ? *x != v : true; +} +template +constexpr auto operator!=(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v != *x)) { + return static_cast(x) ? v != *x : true; +} +template +constexpr auto operator<(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x < v)) { + return static_cast(x) ? *x < v : true; +} +template +constexpr auto operator<(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v < *x)) { + return static_cast(x) ? v < *x : false; +} +template +constexpr auto operator<=(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x <= v)) { + return static_cast(x) ? *x <= v : true; +} +template +constexpr auto operator<=(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v <= *x)) { + return static_cast(x) ? v <= *x : false; +} +template +constexpr auto operator>(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x > v)) { + return static_cast(x) ? *x > v : false; +} +template +constexpr auto operator>(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v > *x)) { + return static_cast(x) ? v > *x : true; +} +template +constexpr auto operator>=(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x >= v)) { + return static_cast(x) ? *x >= v : false; +} +template +constexpr auto operator>=(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v >= *x)) { + return static_cast(x) ? v >= *x : true; +} + +} // namespace absl + +namespace std { + +// std::hash specialization for absl::optional. +template +struct hash> { + size_t operator()(const absl::optional& opt) const { + if (opt) { + return hash()(*opt); + } else { + return static_cast(0x297814aaad196e6dULL); + } + } +}; + +} // namespace std + +#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS +#undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS + +#endif // ABSL_HAVE_STD_OPTIONAL + +#endif // ABSL_TYPES_OPTIONAL_H_ diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc new file mode 100644 index 00000000..25b44b17 --- /dev/null +++ b/absl/types/optional_test.cc @@ -0,0 +1,1539 @@ +// 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. + +#include "absl/types/optional.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" + +namespace { + +std::string TypeQuals(std::string&) { return "&"; } +std::string TypeQuals(std::string&&) { return "&&"; } +std::string TypeQuals(const std::string&) { return "c&"; } +std::string TypeQuals(const std::string&&) { return "c&&"; } + +struct StructorListener { + int construct0 = 0; + int construct1 = 0; + int construct2 = 0; + int listinit = 0; + int copy = 0; + int move = 0; + int copy_assign = 0; + int move_assign = 0; + int destruct = 0; + int volatile_copy = 0; + int volatile_move = 0; + int volatile_copy_assign = 0; + int volatile_move_assign = 0; +}; + +// Suppress MSVC warnings. +// 4521: multiple copy constructors specified +// 4522: multiple assignment operators specified +// We wrote multiple of them to test that the correct overloads are selected. +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4521) +#pragma warning( disable : 4522) +#endif +struct Listenable { + static StructorListener* listener; + + Listenable() { ++listener->construct0; } + explicit Listenable(int /*unused*/) { ++listener->construct1; } + Listenable(int /*unused*/, int /*unused*/) { ++listener->construct2; } + Listenable(std::initializer_list /*unused*/) { ++listener->listinit; } + Listenable(const Listenable& /*unused*/) { ++listener->copy; } + Listenable(const volatile Listenable& /*unused*/) { + ++listener->volatile_copy; + } + Listenable(volatile Listenable&& /*unused*/) { ++listener->volatile_move; } + Listenable(Listenable&& /*unused*/) { ++listener->move; } + Listenable& operator=(const Listenable& /*unused*/) { + ++listener->copy_assign; + return *this; + } + Listenable& operator=(Listenable&& /*unused*/) { + ++listener->move_assign; + return *this; + } + // use void return type instead of volatile T& to work around GCC warning + // when the assignment's returned reference is ignored. + void operator=(const volatile Listenable& /*unused*/) volatile { + ++listener->volatile_copy_assign; + } + void operator=(volatile Listenable&& /*unused*/) volatile { + ++listener->volatile_move_assign; + } + ~Listenable() { ++listener->destruct; } +}; +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +StructorListener* Listenable::listener = nullptr; + +// ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST is defined to 1 when the standard +// library implementation doesn't marked initializer_list's default constructor +// constexpr. The C++11 standard doesn't specify constexpr on it, but C++14 +// added it. However, libstdc++ 4.7 marked it constexpr. +#if defined(_LIBCPP_VERSION) && \ + (_LIBCPP_STD_VER <= 11 || defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)) +#define ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST 1 +#endif + +struct ConstexprType { + enum CtorTypes { + kCtorDefault, + kCtorInt, + kCtorInitializerList, + kCtorConstChar + }; + constexpr ConstexprType() : x(kCtorDefault) {} + constexpr explicit ConstexprType(int i) : x(kCtorInt) {} +#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST + constexpr ConstexprType(std::initializer_list il) + : x(kCtorInitializerList) {} +#endif + constexpr ConstexprType(const char*) // NOLINT(runtime/explicit) + : x(kCtorConstChar) {} + int x; +}; + +struct Copyable { + Copyable() {} + Copyable(const Copyable&) {} + Copyable& operator=(const Copyable&) { return *this; } +}; + +struct MoveableThrow { + MoveableThrow() {} + MoveableThrow(MoveableThrow&&) {} + MoveableThrow& operator=(MoveableThrow&&) { return *this; } +}; + +struct MoveableNoThrow { + MoveableNoThrow() {} + MoveableNoThrow(MoveableNoThrow&&) noexcept {} + MoveableNoThrow& operator=(MoveableNoThrow&&) noexcept { return *this; } +}; + +struct NonMovable { + NonMovable() {} + NonMovable(const NonMovable&) = delete; + NonMovable& operator=(const NonMovable&) = delete; + NonMovable(NonMovable&&) = delete; + NonMovable& operator=(NonMovable&&) = delete; +}; + +TEST(optionalTest, DefaultConstructor) { + absl::optional empty; + EXPECT_FALSE(empty); + constexpr absl::optional cempty; + static_assert(!cempty.has_value(), ""); + EXPECT_TRUE( + std::is_nothrow_default_constructible>::value); +} + +TEST(optionalTest, nulloptConstructor) { + absl::optional empty(absl::nullopt); + EXPECT_FALSE(empty); + +#ifdef ABSL_HAVE_STD_OPTIONAL + constexpr absl::optional cempty{absl::nullopt}; +#else + // Creating a temporary absl::nullopt_t object instead of using absl::nullopt + // because absl::nullopt cannot be constexpr and have external linkage at the + // same time. + constexpr absl::optional cempty{absl::nullopt_t(absl::nullopt_t::init)}; +#endif + static_assert(!cempty.has_value(), ""); + EXPECT_TRUE((std::is_nothrow_constructible, + absl::nullopt_t>::value)); +} + +TEST(optionalTest, CopyConstructor) { + { + absl::optional empty, opt42 = 42; + absl::optional empty_copy(empty); + EXPECT_FALSE(empty_copy); + absl::optional opt42_copy(opt42); + EXPECT_TRUE(opt42_copy); + EXPECT_EQ(42, *opt42_copy); + } + { + absl::optional empty, opt42 = 42; + absl::optional empty_copy(empty); + EXPECT_FALSE(empty_copy); + absl::optional opt42_copy(opt42); + EXPECT_TRUE(opt42_copy); + EXPECT_EQ(42, *opt42_copy); + } + { + absl::optional empty, opt42 = 42; + absl::optional empty_copy(empty); + EXPECT_FALSE(empty_copy); + absl::optional opt42_copy(opt42); + EXPECT_TRUE(opt42_copy); + EXPECT_EQ(42, *opt42_copy); + } + // test copyablility + EXPECT_TRUE(std::is_copy_constructible>::value); + EXPECT_TRUE(std::is_copy_constructible>::value); + EXPECT_FALSE( + std::is_copy_constructible>::value); + EXPECT_FALSE( + std::is_copy_constructible>::value); + EXPECT_FALSE(std::is_copy_constructible>::value); + + EXPECT_FALSE( + absl::is_trivially_copy_constructible>::value); +#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(__GLIBCXX__) + // libstdc++ std::optional implementation (as of 7.2) has a bug: when T is + // trivially copyable, optional is not trivially copyable (due to one of + // its base class is unconditionally nontrivial). +#define ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG 1 +#endif +#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG + EXPECT_TRUE( + absl::is_trivially_copy_constructible>::value); + EXPECT_TRUE( + absl::is_trivially_copy_constructible>::value); +#ifndef _MSC_VER + // See defect report "Trivial copy/move constructor for class with volatile + // member" at + // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2094 + // A class with non-static data member of volatile-qualified type should still + // have a trivial copy constructor if the data member is trivial. + // Also a cv-qualified scalar type should be trivially copyable. + EXPECT_TRUE(absl::is_trivially_copy_constructible< + absl::optional>::value); +#endif // _MSC_VER +#endif // ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG + + // constexpr copy constructor for trivially copyable types + { + constexpr absl::optional o1; + constexpr absl::optional o2 = o1; + static_assert(!o2, ""); + } + { + constexpr absl::optional o1 = 42; + constexpr absl::optional o2 = o1; + static_assert(o2, ""); + static_assert(*o2 == 42, ""); + } + { + struct TrivialCopyable { + constexpr TrivialCopyable() : x(0) {} + constexpr explicit TrivialCopyable(int i) : x(i) {} + int x; + }; + constexpr absl::optional o1(42); + constexpr absl::optional o2 = o1; + static_assert(o2, ""); + static_assert(o2->x == 42, ""); +#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG + EXPECT_TRUE(absl::is_trivially_copy_constructible< + absl::optional>::value); + EXPECT_TRUE(absl::is_trivially_copy_constructible< + absl::optional>::value); +#endif + EXPECT_FALSE(std::is_copy_constructible< + absl::optional>::value); + } +} + +TEST(optionalTest, MoveConstructor) { + absl::optional empty, opt42 = 42; + absl::optional empty_move(std::move(empty)); + EXPECT_FALSE(empty_move); + absl::optional opt42_move(std::move(opt42)); + EXPECT_TRUE(opt42_move); + EXPECT_EQ(42, opt42_move); + // test movability + EXPECT_TRUE(std::is_move_constructible>::value); + EXPECT_TRUE(std::is_move_constructible>::value); + EXPECT_TRUE(std::is_move_constructible>::value); + EXPECT_TRUE( + std::is_move_constructible>::value); + EXPECT_FALSE(std::is_move_constructible>::value); + // test noexcept + EXPECT_TRUE(std::is_nothrow_move_constructible>::value); +#ifndef ABSL_HAVE_STD_OPTIONAL + EXPECT_EQ( + absl::default_allocator_is_nothrow::value, + std::is_nothrow_move_constructible>::value); +#endif + EXPECT_TRUE(std::is_nothrow_move_constructible< + absl::optional>::value); +} + +TEST(optionalTest, Destructor) { + struct Trivial {}; + + struct NonTrivial { + NonTrivial(const NonTrivial&) {} + NonTrivial& operator=(const NonTrivial&) { return *this; } + ~NonTrivial() {} + }; + + EXPECT_TRUE(std::is_trivially_destructible>::value); + EXPECT_TRUE(std::is_trivially_destructible>::value); + EXPECT_FALSE( + std::is_trivially_destructible>::value); +} + +TEST(optionalTest, InPlaceConstructor) { + constexpr absl::optional opt0{absl::in_place_t()}; + static_assert(opt0, ""); + static_assert(opt0->x == ConstexprType::kCtorDefault, ""); + constexpr absl::optional opt1{absl::in_place_t(), 1}; + static_assert(opt1, ""); + static_assert(opt1->x == ConstexprType::kCtorInt, ""); +#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST + constexpr absl::optional opt2{absl::in_place_t(), {1, 2}}; + static_assert(opt2, ""); + static_assert(opt2->x == ConstexprType::kCtorInitializerList, ""); +#endif + + // TODO(b/34201852): uncomment these when std::is_constructible + // SFINAE is added to optional::optional(absl::in_place_t, Args&&...). + // struct I { + // I(absl::in_place_t); + // }; + + // EXPECT_FALSE((std::is_constructible, + // absl::in_place_t>::value)); + // EXPECT_FALSE((std::is_constructible, const + // absl::in_place_t&>::value)); +} + +// template optional(U&&); +TEST(optionalTest, ValueConstructor) { + constexpr absl::optional opt0(0); + static_assert(opt0, ""); + static_assert(*opt0 == 0, ""); + EXPECT_TRUE((std::is_convertible>::value)); + // Copy initialization ( = "abc") won't work due to optional(optional&&) + // is not constexpr. Use list initialization instead. This invokes + // absl::optional::absl::optional(U&&), with U = const char + // (&) [4], which direct-initializes the ConstexprType value held by the + // optional via ConstexprType::ConstexprType(const char*). + constexpr absl::optional opt1 = {"abc"}; + static_assert(opt1, ""); + static_assert(ConstexprType::kCtorConstChar == opt1->x, ""); + EXPECT_TRUE( + (std::is_convertible>::value)); + // direct initialization + constexpr absl::optional opt2{2}; + static_assert(opt2, ""); + static_assert(ConstexprType::kCtorInt == opt2->x, ""); + EXPECT_FALSE( + (std::is_convertible>::value)); + + // this invokes absl::optional::optional(int&&) + // NOTE: this has different behavior than assignment, e.g. + // "opt3 = {};" clears the optional rather than setting the value to 0 + // According to C++17 standard N4659 [over.ics.list] 16.3.3.1.5, (9.2)- "if + // the initializer list has no elements, the implicit conversion is the + // identity conversion", so `optional(int&&)` should be a better match than + // `optional(optional&&)` which is a user-defined conversion. + // Note: GCC 7 has a bug with this overload selection when compiled with + // `-std=c++17`. +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 7 && \ + __cplusplus == 201703L +#define ABSL_GCC7_OVER_ICS_LIST_BUG 1 +#endif +#ifndef ABSL_GCC7_OVER_ICS_LIST_BUG + constexpr absl::optional opt3({}); + static_assert(opt3, ""); + static_assert(*opt3 == 0, ""); +#endif + + // this invokes the move constructor with a default constructed optional + // because non-template function is a better match than template function. + absl::optional opt4({}); + EXPECT_FALSE(opt4); +} + +struct Implicit {}; + +struct Explicit {}; + +struct Convert { + Convert(const Implicit&) // NOLINT(runtime/explicit) + : implicit(true), move(false) {} + Convert(Implicit&&) // NOLINT(runtime/explicit) + : implicit(true), move(true) {} + explicit Convert(const Explicit&) : implicit(false), move(false) {} + explicit Convert(Explicit&&) : implicit(false), move(true) {} + + bool implicit; + bool move; +}; + +struct ConvertFromOptional { + ConvertFromOptional(const Implicit&) // NOLINT(runtime/explicit) + : implicit(true), move(false), from_optional(false) {} + ConvertFromOptional(Implicit&&) // NOLINT(runtime/explicit) + : implicit(true), move(true), from_optional(false) {} + ConvertFromOptional( + const absl::optional&) // NOLINT(runtime/explicit) + : implicit(true), move(false), from_optional(true) {} + ConvertFromOptional(absl::optional&&) // NOLINT(runtime/explicit) + : implicit(true), move(true), from_optional(true) {} + explicit ConvertFromOptional(const Explicit&) + : implicit(false), move(false), from_optional(false) {} + explicit ConvertFromOptional(Explicit&&) + : implicit(false), move(true), from_optional(false) {} + explicit ConvertFromOptional(const absl::optional&) + : implicit(false), move(false), from_optional(true) {} + explicit ConvertFromOptional(absl::optional&&) + : implicit(false), move(true), from_optional(true) {} + + bool implicit; + bool move; + bool from_optional; +}; + +TEST(optionalTest, ConvertingConstructor) { + absl::optional i_empty; + absl::optional i(absl::in_place); + absl::optional e_empty; + absl::optional e(absl::in_place); + { + // implicitly constructing absl::optional from + // absl::optional + absl::optional empty = i_empty; + EXPECT_FALSE(empty); + absl::optional opt_copy = i; + EXPECT_TRUE(opt_copy); + EXPECT_TRUE(opt_copy->implicit); + EXPECT_FALSE(opt_copy->move); + absl::optional opt_move = absl::optional(absl::in_place); + EXPECT_TRUE(opt_move); + EXPECT_TRUE(opt_move->implicit); + EXPECT_TRUE(opt_move->move); + } + { + // explicitly constructing absl::optional from + // absl::optional + absl::optional empty(e_empty); + EXPECT_FALSE(empty); + absl::optional opt_copy(e); + EXPECT_TRUE(opt_copy); + EXPECT_FALSE(opt_copy->implicit); + EXPECT_FALSE(opt_copy->move); + EXPECT_FALSE((std::is_convertible&, + absl::optional>::value)); + absl::optional opt_move{absl::optional(absl::in_place)}; + EXPECT_TRUE(opt_move); + EXPECT_FALSE(opt_move->implicit); + EXPECT_TRUE(opt_move->move); + EXPECT_FALSE((std::is_convertible&&, + absl::optional>::value)); + } + { + // implicitly constructing absl::optional from + // absl::optional via + // ConvertFromOptional(absl::optional&&) check that + // ConvertFromOptional(Implicit&&) is NOT called + static_assert( + std::is_convertible, + absl::optional>::value, + ""); + absl::optional opt0 = i_empty; + EXPECT_TRUE(opt0); + EXPECT_TRUE(opt0->implicit); + EXPECT_FALSE(opt0->move); + EXPECT_TRUE(opt0->from_optional); + absl::optional opt1 = absl::optional(); + EXPECT_TRUE(opt1); + EXPECT_TRUE(opt1->implicit); + EXPECT_TRUE(opt1->move); + EXPECT_TRUE(opt1->from_optional); + } + { + // implicitly constructing absl::optional from + // absl::optional via + // ConvertFromOptional(absl::optional&&) check that + // ConvertFromOptional(Explicit&&) is NOT called + absl::optional opt0(e_empty); + EXPECT_TRUE(opt0); + EXPECT_FALSE(opt0->implicit); + EXPECT_FALSE(opt0->move); + EXPECT_TRUE(opt0->from_optional); + EXPECT_FALSE( + (std::is_convertible&, + absl::optional>::value)); + absl::optional opt1{absl::optional()}; + EXPECT_TRUE(opt1); + EXPECT_FALSE(opt1->implicit); + EXPECT_TRUE(opt1->move); + EXPECT_TRUE(opt1->from_optional); + EXPECT_FALSE( + (std::is_convertible&&, + absl::optional>::value)); + } +} + +TEST(optionalTest, StructorBasic) { + StructorListener listener; + Listenable::listener = &listener; + { + absl::optional empty; + EXPECT_FALSE(empty); + absl::optional opt0(absl::in_place); + EXPECT_TRUE(opt0); + absl::optional opt1(absl::in_place, 1); + EXPECT_TRUE(opt1); + absl::optional opt2(absl::in_place, 1, 2); + EXPECT_TRUE(opt2); + } + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(1, listener.construct1); + EXPECT_EQ(1, listener.construct2); + EXPECT_EQ(3, listener.destruct); +} + +TEST(optionalTest, CopyMoveStructor) { + StructorListener listener; + Listenable::listener = &listener; + absl::optional original(absl::in_place); + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(0, listener.copy); + EXPECT_EQ(0, listener.move); + absl::optional copy(original); + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(1, listener.copy); + EXPECT_EQ(0, listener.move); + absl::optional move(std::move(original)); + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(1, listener.copy); + EXPECT_EQ(1, listener.move); +} + +TEST(optionalTest, ListInit) { + StructorListener listener; + Listenable::listener = &listener; + absl::optional listinit1(absl::in_place, {1}); + absl::optional listinit2(absl::in_place, {1, 2}); + EXPECT_EQ(2, listener.listinit); +} + +TEST(optionalTest, AssignFromNullopt) { + absl::optional opt(1); + opt = absl::nullopt; + EXPECT_FALSE(opt); + + StructorListener listener; + Listenable::listener = &listener; + absl::optional opt1(absl::in_place); + opt1 = absl::nullopt; + EXPECT_FALSE(opt1); + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(1, listener.destruct); + + EXPECT_TRUE(( + std::is_nothrow_assignable, absl::nullopt_t>::value)); + EXPECT_TRUE((std::is_nothrow_assignable, + absl::nullopt_t>::value)); +} + +TEST(optionalTest, CopyAssignment) { + const absl::optional empty, opt1 = 1, opt2 = 2; + absl::optional empty_to_opt1, opt1_to_opt2, opt2_to_empty; + + EXPECT_FALSE(empty_to_opt1); + empty_to_opt1 = empty; + EXPECT_FALSE(empty_to_opt1); + empty_to_opt1 = opt1; + EXPECT_TRUE(empty_to_opt1); + EXPECT_EQ(1, empty_to_opt1.value()); + + EXPECT_FALSE(opt1_to_opt2); + opt1_to_opt2 = opt1; + EXPECT_TRUE(opt1_to_opt2); + EXPECT_EQ(1, opt1_to_opt2.value()); + opt1_to_opt2 = opt2; + EXPECT_TRUE(opt1_to_opt2); + EXPECT_EQ(2, opt1_to_opt2.value()); + + EXPECT_FALSE(opt2_to_empty); + opt2_to_empty = opt2; + EXPECT_TRUE(opt2_to_empty); + EXPECT_EQ(2, opt2_to_empty.value()); + opt2_to_empty = empty; + EXPECT_FALSE(opt2_to_empty); + + EXPECT_FALSE(std::is_copy_assignable>::value); + EXPECT_TRUE(std::is_copy_assignable>::value); + EXPECT_FALSE(std::is_copy_assignable>::value); + EXPECT_FALSE(std::is_copy_assignable>::value); + EXPECT_FALSE(std::is_copy_assignable>::value); + + EXPECT_TRUE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_copy_assignable::value); + + struct Trivial { + int i; + }; + struct NonTrivial { + NonTrivial& operator=(const NonTrivial&) { return *this; } + int i; + }; + + EXPECT_TRUE(absl::is_trivially_copy_assignable::value); + EXPECT_FALSE(std::is_copy_assignable::value); + EXPECT_FALSE(std::is_copy_assignable::value); + EXPECT_TRUE(std::is_copy_assignable::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable::value); + + // std::optional doesn't support volatile nontrivial types. +#ifndef ABSL_HAVE_STD_OPTIONAL + { + StructorListener listener; + Listenable::listener = &listener; + + absl::optional empty, set(absl::in_place); + EXPECT_EQ(1, listener.construct0); + absl::optional empty_to_empty, empty_to_set, + set_to_empty(absl::in_place), set_to_set(absl::in_place); + EXPECT_EQ(3, listener.construct0); + empty_to_empty = empty; // no effect + empty_to_set = set; // copy construct + set_to_empty = empty; // destruct + set_to_set = set; // copy assign + EXPECT_EQ(1, listener.volatile_copy); + EXPECT_EQ(0, listener.volatile_move); + EXPECT_EQ(1, listener.destruct); + EXPECT_EQ(1, listener.volatile_copy_assign); + } +#endif // ABSL_HAVE_STD_OPTIONAL +} + +TEST(optionalTest, MoveAssignment) { + { + StructorListener listener; + Listenable::listener = &listener; + + absl::optional empty1, empty2, set1(absl::in_place), + set2(absl::in_place); + EXPECT_EQ(2, listener.construct0); + absl::optional empty_to_empty, empty_to_set, + set_to_empty(absl::in_place), set_to_set(absl::in_place); + EXPECT_EQ(4, listener.construct0); + empty_to_empty = std::move(empty1); + empty_to_set = std::move(set1); + set_to_empty = std::move(empty2); + set_to_set = std::move(set2); + EXPECT_EQ(0, listener.copy); + EXPECT_EQ(1, listener.move); + EXPECT_EQ(1, listener.destruct); + EXPECT_EQ(1, listener.move_assign); + } + // std::optional doesn't support volatile nontrivial types. +#ifndef ABSL_HAVE_STD_OPTIONAL + { + StructorListener listener; + Listenable::listener = &listener; + + absl::optional empty1, empty2, set1(absl::in_place), + set2(absl::in_place); + EXPECT_EQ(2, listener.construct0); + absl::optional empty_to_empty, empty_to_set, + set_to_empty(absl::in_place), set_to_set(absl::in_place); + EXPECT_EQ(4, listener.construct0); + empty_to_empty = std::move(empty1); // no effect + empty_to_set = std::move(set1); // move construct + set_to_empty = std::move(empty2); // destruct + set_to_set = std::move(set2); // move assign + EXPECT_EQ(0, listener.volatile_copy); + EXPECT_EQ(1, listener.volatile_move); + EXPECT_EQ(1, listener.destruct); + EXPECT_EQ(1, listener.volatile_move_assign); + } +#endif // ABSL_HAVE_STD_OPTIONAL + EXPECT_FALSE(std::is_move_assignable>::value); + EXPECT_TRUE(std::is_move_assignable>::value); + EXPECT_TRUE(std::is_move_assignable>::value); + EXPECT_TRUE(std::is_move_assignable>::value); + EXPECT_FALSE(std::is_move_assignable>::value); + + EXPECT_FALSE( + std::is_nothrow_move_assignable>::value); + EXPECT_TRUE( + std::is_nothrow_move_assignable>::value); +} + +struct NoConvertToOptional { + // disable implicit conversion from const NoConvertToOptional& + // to absl::optional. + NoConvertToOptional(const NoConvertToOptional&) = delete; +}; + +struct CopyConvert { + CopyConvert(const NoConvertToOptional&); + CopyConvert& operator=(const CopyConvert&) = delete; + CopyConvert& operator=(const NoConvertToOptional&); +}; + +struct CopyConvertFromOptional { + CopyConvertFromOptional(const NoConvertToOptional&); + CopyConvertFromOptional(const absl::optional&); + CopyConvertFromOptional& operator=(const CopyConvertFromOptional&) = delete; + CopyConvertFromOptional& operator=(const NoConvertToOptional&); + CopyConvertFromOptional& operator=( + const absl::optional&); +}; + +struct MoveConvert { + MoveConvert(NoConvertToOptional&&); + MoveConvert& operator=(const MoveConvert&) = delete; + MoveConvert& operator=(NoConvertToOptional&&); +}; + +struct MoveConvertFromOptional { + MoveConvertFromOptional(NoConvertToOptional&&); + MoveConvertFromOptional(absl::optional&&); + MoveConvertFromOptional& operator=(const MoveConvertFromOptional&) = delete; + MoveConvertFromOptional& operator=(NoConvertToOptional&&); + MoveConvertFromOptional& operator=(absl::optional&&); +}; + +// template absl::optional& operator=(U&& v); +TEST(optionalTest, ValueAssignment) { + absl::optional opt; + EXPECT_FALSE(opt); + opt = 42; + EXPECT_TRUE(opt); + EXPECT_EQ(42, opt.value()); + opt = absl::nullopt; + EXPECT_FALSE(opt); + opt = 42; + EXPECT_TRUE(opt); + EXPECT_EQ(42, opt.value()); + opt = 43; + EXPECT_TRUE(opt); + EXPECT_EQ(43, opt.value()); + opt = {}; // this should clear optional + EXPECT_FALSE(opt); + + opt = {44}; + EXPECT_TRUE(opt); + EXPECT_EQ(44, opt.value()); + + // U = const NoConvertToOptional& + EXPECT_TRUE((std::is_assignable&, + const NoConvertToOptional&>::value)); + // U = const absl::optional& + EXPECT_TRUE((std::is_assignable&, + const NoConvertToOptional&>::value)); + // U = const NoConvertToOptional& triggers SFINAE because + // std::is_constructible_v is false + EXPECT_FALSE((std::is_assignable&, + const NoConvertToOptional&>::value)); + // U = NoConvertToOptional + EXPECT_TRUE((std::is_assignable&, + NoConvertToOptional&&>::value)); + // U = const NoConvertToOptional& triggers SFINAE because + // std::is_constructible_v is false + EXPECT_FALSE((std::is_assignable&, + const NoConvertToOptional&>::value)); + // U = NoConvertToOptional + EXPECT_TRUE((std::is_assignable&, + NoConvertToOptional&&>::value)); + // U = const absl::optional& + EXPECT_TRUE( + (std::is_assignable&, + const absl::optional&>::value)); + // U = absl::optional + EXPECT_TRUE( + (std::is_assignable&, + absl::optional&&>::value)); +} + +// template absl::optional& operator=(const absl::optional& +// rhs); template absl::optional& operator=(absl::optional&& +// rhs); +TEST(optionalTest, ConvertingAssignment) { + absl::optional opt_i; + absl::optional opt_c('c'); + opt_i = opt_c; + EXPECT_TRUE(opt_i); + EXPECT_EQ(*opt_c, *opt_i); + opt_i = absl::optional(); + EXPECT_FALSE(opt_i); + opt_i = absl::optional('d'); + EXPECT_TRUE(opt_i); + EXPECT_EQ('d', *opt_i); + + absl::optional opt_str; + absl::optional opt_cstr("abc"); + opt_str = opt_cstr; + EXPECT_TRUE(opt_str); + EXPECT_EQ(std::string("abc"), *opt_str); + opt_str = absl::optional(); + EXPECT_FALSE(opt_str); + opt_str = absl::optional("def"); + EXPECT_TRUE(opt_str); + EXPECT_EQ(std::string("def"), *opt_str); + + // operator=(const absl::optional&) with U = NoConvertToOptional + EXPECT_TRUE( + (std::is_assignable, + const absl::optional&>::value)); + // operator=(const absl::optional&) with U = NoConvertToOptional + // triggers SFINAE because + // std::is_constructible_v is false + EXPECT_FALSE( + (std::is_assignable&, + const absl::optional&>::value)); + // operator=(absl::optional&&) with U = NoConvertToOptional + EXPECT_TRUE( + (std::is_assignable&, + absl::optional&&>::value)); + // operator=(const absl::optional&) with U = NoConvertToOptional triggers + // SFINAE because std::is_constructible_v is false. operator=(U&&) with U = const + // absl::optional& triggers SFINAE because + // std::is_constructible&&> is true. + EXPECT_FALSE( + (std::is_assignable&, + const absl::optional&>::value)); +} + +TEST(optionalTest, ResetAndHasValue) { + StructorListener listener; + Listenable::listener = &listener; + absl::optional opt; + EXPECT_FALSE(opt); + EXPECT_FALSE(opt.has_value()); + opt.emplace(); + EXPECT_TRUE(opt); + EXPECT_TRUE(opt.has_value()); + opt.reset(); + EXPECT_FALSE(opt); + EXPECT_FALSE(opt.has_value()); + EXPECT_EQ(1, listener.destruct); + opt.reset(); + EXPECT_FALSE(opt); + EXPECT_FALSE(opt.has_value()); + + constexpr absl::optional empty; + static_assert(!empty.has_value(), ""); + constexpr absl::optional nonempty(1); + static_assert(nonempty.has_value(), ""); +} + +TEST(optionalTest, Emplace) { + StructorListener listener; + Listenable::listener = &listener; + absl::optional opt; + EXPECT_FALSE(opt); + opt.emplace(1); + EXPECT_TRUE(opt); + opt.emplace(1, 2); + EXPECT_EQ(1, listener.construct1); + EXPECT_EQ(1, listener.construct2); + EXPECT_EQ(1, listener.destruct); + + absl::optional o; + EXPECT_TRUE((std::is_same::value)); + std::string& ref = o.emplace("abc"); + EXPECT_EQ(&ref, &o.value()); +} + +TEST(optionalTest, ListEmplace) { + StructorListener listener; + Listenable::listener = &listener; + absl::optional opt; + EXPECT_FALSE(opt); + opt.emplace({1}); + EXPECT_TRUE(opt); + opt.emplace({1, 2}); + EXPECT_EQ(2, listener.listinit); + EXPECT_EQ(1, listener.destruct); + + absl::optional o; + EXPECT_TRUE((std::is_same::value)); + Listenable& ref = o.emplace({1}); + EXPECT_EQ(&ref, &o.value()); +} + +TEST(optionalTest, Swap) { + absl::optional opt_empty, opt1 = 1, opt2 = 2; + EXPECT_FALSE(opt_empty); + EXPECT_TRUE(opt1); + EXPECT_EQ(1, opt1.value()); + EXPECT_TRUE(opt2); + EXPECT_EQ(2, opt2.value()); + swap(opt_empty, opt1); + EXPECT_FALSE(opt1); + EXPECT_TRUE(opt_empty); + EXPECT_EQ(1, opt_empty.value()); + EXPECT_TRUE(opt2); + EXPECT_EQ(2, opt2.value()); + swap(opt_empty, opt1); + EXPECT_FALSE(opt_empty); + EXPECT_TRUE(opt1); + EXPECT_EQ(1, opt1.value()); + EXPECT_TRUE(opt2); + EXPECT_EQ(2, opt2.value()); + swap(opt1, opt2); + EXPECT_FALSE(opt_empty); + EXPECT_TRUE(opt1); + EXPECT_EQ(2, opt1.value()); + EXPECT_TRUE(opt2); + EXPECT_EQ(1, opt2.value()); + + EXPECT_TRUE(noexcept(opt1.swap(opt2))); + EXPECT_TRUE(noexcept(swap(opt1, opt2))); +} + +TEST(optionalTest, PointerStuff) { + absl::optional opt(absl::in_place, "foo"); + EXPECT_EQ("foo", *opt); + const auto& opt_const = opt; + EXPECT_EQ("foo", *opt_const); + EXPECT_EQ(opt->size(), 3); + EXPECT_EQ(opt_const->size(), 3); + + constexpr absl::optional opt1(1); + static_assert(opt1->x == ConstexprType::kCtorInt, ""); +} + +// gcc has a bug pre 4.9.1 where it doesn't do correct overload resolution +// when overloads are const-qualified and *this is an raluve. +// Skip that test to make the build green again when using the old compiler. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59296 is fixed in 4.9.1. +#if defined(__GNUC__) && !defined(__clang__) +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#if GCC_VERSION < 40901 +#define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG +#endif +#endif + +// MSVC has a bug with "cv-qualifiers in class construction", fixed in 2017. See +// https://docs.microsoft.com/en-us/cpp/cpp-conformance-improvements-2017#bug-fixes +// The compiler some incorrectly ingores the cv-qualifier when generating a +// class object via a constructor call. For example: +// +// class optional { +// constexpr T&& value() &&; +// constexpr const T&& value() const &&; +// } +// +// using COI = const absl::optional; +// static_assert(2 == COI(2).value(), ""); // const && +// +// This should invoke the "const &&" overload but since it ignores the const +// qualifier it finds the "&&" overload the best candidate. +#if defined(_MSC_VER) && _MSC_VER < 1910 +#define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG +#endif + +TEST(optionalTest, Value) { + using O = absl::optional; + using CO = const absl::optional; + using OC = absl::optional; + O lvalue(absl::in_place, "lvalue"); + CO clvalue(absl::in_place, "clvalue"); + OC lvalue_c(absl::in_place, "lvalue_c"); + EXPECT_EQ("lvalue", lvalue.value()); + EXPECT_EQ("clvalue", clvalue.value()); + EXPECT_EQ("lvalue_c", lvalue_c.value()); + EXPECT_EQ("xvalue", O(absl::in_place, "xvalue").value()); + EXPECT_EQ("xvalue_c", OC(absl::in_place, "xvalue_c").value()); +#ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG + EXPECT_EQ("cxvalue", CO(absl::in_place, "cxvalue").value()); +#endif + EXPECT_EQ("&", TypeQuals(lvalue.value())); + EXPECT_EQ("c&", TypeQuals(clvalue.value())); + EXPECT_EQ("c&", TypeQuals(lvalue_c.value())); + EXPECT_EQ("&&", TypeQuals(O(absl::in_place, "xvalue").value())); +#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ + !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) + EXPECT_EQ("c&&", TypeQuals(CO(absl::in_place, "cxvalue").value())); +#endif + EXPECT_EQ("c&&", TypeQuals(OC(absl::in_place, "xvalue_c").value())); + + // test on volatile type + using OV = absl::optional; + OV lvalue_v(absl::in_place, 42); + EXPECT_EQ(42, lvalue_v.value()); + EXPECT_EQ(42, OV(42).value()); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + // test exception throw on value() + absl::optional empty; +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(empty.value(), absl::bad_optional_access); +#else + EXPECT_DEATH(empty.value(), "Bad optional access"); +#endif + + // test constexpr value() + constexpr absl::optional o1(1); + static_assert(1 == o1.value(), ""); // const & +#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ + !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) + using COI = const absl::optional; + static_assert(2 == COI(2).value(), ""); // const && +#endif +} + +TEST(optionalTest, DerefOperator) { + using O = absl::optional; + using CO = const absl::optional; + using OC = absl::optional; + O lvalue(absl::in_place, "lvalue"); + CO clvalue(absl::in_place, "clvalue"); + OC lvalue_c(absl::in_place, "lvalue_c"); + EXPECT_EQ("lvalue", *lvalue); + EXPECT_EQ("clvalue", *clvalue); + EXPECT_EQ("lvalue_c", *lvalue_c); + EXPECT_EQ("xvalue", *O(absl::in_place, "xvalue")); + EXPECT_EQ("xvalue_c", *OC(absl::in_place, "xvalue_c")); +#ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG + EXPECT_EQ("cxvalue", *CO(absl::in_place, "cxvalue")); +#endif + EXPECT_EQ("&", TypeQuals(*lvalue)); + EXPECT_EQ("c&", TypeQuals(*clvalue)); + EXPECT_EQ("&&", TypeQuals(*O(absl::in_place, "xvalue"))); +#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ + !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) + EXPECT_EQ("c&&", TypeQuals(*CO(absl::in_place, "cxvalue"))); +#endif + EXPECT_EQ("c&&", TypeQuals(*OC(absl::in_place, "xvalue_c"))); + + // test on volatile type + using OV = absl::optional; + OV lvalue_v(absl::in_place, 42); + EXPECT_EQ(42, *lvalue_v); + EXPECT_EQ(42, *OV(42)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + constexpr absl::optional opt1(1); + static_assert(*opt1 == 1, ""); +#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ + !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) + using COI = const absl::optional; + static_assert(*COI(2) == 2, ""); +#endif +} + +TEST(optionalTest, ValueOr) { + absl::optional opt_empty, opt_set = 1.2; + EXPECT_EQ(42.0, opt_empty.value_or(42)); + EXPECT_EQ(1.2, opt_set.value_or(42)); + EXPECT_EQ(42.0, absl::optional().value_or(42)); + EXPECT_EQ(1.2, absl::optional(1.2).value_or(42)); + + constexpr absl::optional copt_empty, copt_set = {1.2}; + static_assert(42.0 == copt_empty.value_or(42), ""); + static_assert(1.2 == copt_set.value_or(42), ""); +#ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG + using COD = const absl::optional; + static_assert(42.0 == COD().value_or(42), ""); + static_assert(1.2 == COD(1.2).value_or(42), ""); +#endif +} + +// make_optional cannot be constexpr until C++17 +TEST(optionalTest, make_optional) { + auto opt_int = absl::make_optional(42); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_EQ(42, opt_int); + + StructorListener listener; + Listenable::listener = &listener; + + absl::optional opt0 = absl::make_optional(); + EXPECT_EQ(1, listener.construct0); + absl::optional opt1 = absl::make_optional(1); + EXPECT_EQ(1, listener.construct1); + absl::optional opt2 = absl::make_optional(1, 2); + EXPECT_EQ(1, listener.construct2); + absl::optional opt3 = absl::make_optional({1}); + absl::optional opt4 = absl::make_optional({1, 2}); + EXPECT_EQ(2, listener.listinit); + + // Constexpr tests on trivially copyable types + // optional has trivial copy/move ctors when T is trivially copyable. + // For nontrivial types with constexpr constructors, we need copy elision in + // C++17 for make_optional to be constexpr. + { + constexpr absl::optional c_opt = absl::make_optional(42); + static_assert(c_opt.value() == 42, ""); + } + { + struct TrivialCopyable { + constexpr TrivialCopyable() : x(0) {} + constexpr explicit TrivialCopyable(int i) : x(i) {} + int x; + }; + + constexpr TrivialCopyable v; + constexpr absl::optional c_opt0 = absl::make_optional(v); + static_assert(c_opt0->x == 0, ""); + constexpr absl::optional c_opt1 = + absl::make_optional(); + static_assert(c_opt1->x == 0, ""); + constexpr absl::optional c_opt2 = + absl::make_optional(42); + static_assert(c_opt2->x == 42, ""); + } +} + +template +void optionalTest_Comparisons_EXPECT_LESS(T x, U y) { + EXPECT_FALSE(x == y); + EXPECT_TRUE(x != y); + EXPECT_TRUE(x < y); + EXPECT_FALSE(x > y); + EXPECT_TRUE(x <= y); + EXPECT_FALSE(x >= y); +} + +template +void optionalTest_Comparisons_EXPECT_SAME(T x, U y) { + EXPECT_TRUE(x == y); + EXPECT_FALSE(x != y); + EXPECT_FALSE(x < y); + EXPECT_FALSE(x > y); + EXPECT_TRUE(x <= y); + EXPECT_TRUE(x >= y); +} + +template +void optionalTest_Comparisons_EXPECT_GREATER(T x, U y) { + EXPECT_FALSE(x == y); + EXPECT_TRUE(x != y); + EXPECT_FALSE(x < y); + EXPECT_TRUE(x > y); + EXPECT_FALSE(x <= y); + EXPECT_TRUE(x >= y); +} + + +template +void TestComparisons() { + absl::optional ae, a2{2}, a4{4}; + absl::optional be, b2{2}, b4{4}; + V v3 = 3; + + // LHS: absl::nullopt, ae, a2, v3, a4 + // RHS: absl::nullopt, be, b2, v3, b4 + + // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,absl::nullopt); + optionalTest_Comparisons_EXPECT_SAME(absl::nullopt, be); + optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b2); + // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,v3); + optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b4); + + optionalTest_Comparisons_EXPECT_SAME(ae, absl::nullopt); + optionalTest_Comparisons_EXPECT_SAME(ae, be); + optionalTest_Comparisons_EXPECT_LESS(ae, b2); + optionalTest_Comparisons_EXPECT_LESS(ae, v3); + optionalTest_Comparisons_EXPECT_LESS(ae, b4); + + optionalTest_Comparisons_EXPECT_GREATER(a2, absl::nullopt); + optionalTest_Comparisons_EXPECT_GREATER(a2, be); + optionalTest_Comparisons_EXPECT_SAME(a2, b2); + optionalTest_Comparisons_EXPECT_LESS(a2, v3); + optionalTest_Comparisons_EXPECT_LESS(a2, b4); + + // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(v3,absl::nullopt); + optionalTest_Comparisons_EXPECT_GREATER(v3, be); + optionalTest_Comparisons_EXPECT_GREATER(v3, b2); + optionalTest_Comparisons_EXPECT_SAME(v3, v3); + optionalTest_Comparisons_EXPECT_LESS(v3, b4); + + optionalTest_Comparisons_EXPECT_GREATER(a4, absl::nullopt); + optionalTest_Comparisons_EXPECT_GREATER(a4, be); + optionalTest_Comparisons_EXPECT_GREATER(a4, b2); + optionalTest_Comparisons_EXPECT_GREATER(a4, v3); + optionalTest_Comparisons_EXPECT_SAME(a4, b4); +} + +struct Int1 { + Int1() = default; + Int1(int i) : i(i) {} // NOLINT(runtime/explicit) + int i; +}; + +struct Int2 { + Int2() = default; + Int2(int i) : i(i) {} // NOLINT(runtime/explicit) + int i; +}; + +// comparison between Int1 and Int2 +constexpr bool operator==(const Int1& lhs, const Int2& rhs) { + return lhs.i == rhs.i; +} +constexpr bool operator!=(const Int1& lhs, const Int2& rhs) { + return !(lhs == rhs); +} +constexpr bool operator<(const Int1& lhs, const Int2& rhs) { + return lhs.i < rhs.i; +} +constexpr bool operator<=(const Int1& lhs, const Int2& rhs) { + return lhs < rhs || lhs == rhs; +} +constexpr bool operator>(const Int1& lhs, const Int2& rhs) { + return !(lhs <= rhs); +} +constexpr bool operator>=(const Int1& lhs, const Int2& rhs) { + return !(lhs < rhs); +} + +TEST(optionalTest, Comparisons) { + TestComparisons(); + TestComparisons(); + TestComparisons(); + TestComparisons(); + TestComparisons(); + + // compare absl::optional with const char* + absl::optional opt_str = "abc"; + const char* cstr = "abc"; + EXPECT_TRUE(opt_str == cstr); + // compare absl::optional with absl::optional + absl::optional opt_cstr = cstr; + EXPECT_TRUE(opt_str == opt_cstr); + // compare absl::optional with absl::optional + absl::optional e1; + absl::optional e2; + EXPECT_TRUE(e1 == e2); +} + + +TEST(optionalTest, SwapRegression) { + StructorListener listener; + Listenable::listener = &listener; + + { + absl::optional a; + absl::optional b(absl::in_place); + a.swap(b); + } + + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(1, listener.move); + EXPECT_EQ(2, listener.destruct); + + { + absl::optional a(absl::in_place); + absl::optional b; + a.swap(b); + } + + EXPECT_EQ(2, listener.construct0); + EXPECT_EQ(2, listener.move); + EXPECT_EQ(4, listener.destruct); +} + +TEST(optionalTest, BigStringLeakCheck) { + constexpr size_t n = 1 << 16; + + using OS = absl::optional; + + OS a; + OS b = absl::nullopt; + OS c = std::string(n, 'c'); + std::string sd(n, 'd'); + OS d = sd; + OS e(absl::in_place, n, 'e'); + OS f; + f.emplace(n, 'f'); + + OS ca(a); + OS cb(b); + OS cc(c); + OS cd(d); + OS ce(e); + + OS oa; + OS ob = absl::nullopt; + OS oc = std::string(n, 'c'); + std::string sod(n, 'd'); + OS od = sod; + OS oe(absl::in_place, n, 'e'); + OS of; + of.emplace(n, 'f'); + + OS ma(std::move(oa)); + OS mb(std::move(ob)); + OS mc(std::move(oc)); + OS md(std::move(od)); + OS me(std::move(oe)); + OS mf(std::move(of)); + + OS aa1; + OS ab1 = absl::nullopt; + OS ac1 = std::string(n, 'c'); + std::string sad1(n, 'd'); + OS ad1 = sad1; + OS ae1(absl::in_place, n, 'e'); + OS af1; + af1.emplace(n, 'f'); + + OS aa2; + OS ab2 = absl::nullopt; + OS ac2 = std::string(n, 'c'); + std::string sad2(n, 'd'); + OS ad2 = sad2; + OS ae2(absl::in_place, n, 'e'); + OS af2; + af2.emplace(n, 'f'); + + aa1 = af2; + ab1 = ae2; + ac1 = ad2; + ad1 = ac2; + ae1 = ab2; + af1 = aa2; + + OS aa3; + OS ab3 = absl::nullopt; + OS ac3 = std::string(n, 'c'); + std::string sad3(n, 'd'); + OS ad3 = sad3; + OS ae3(absl::in_place, n, 'e'); + OS af3; + af3.emplace(n, 'f'); + + aa3 = absl::nullopt; + ab3 = absl::nullopt; + ac3 = absl::nullopt; + ad3 = absl::nullopt; + ae3 = absl::nullopt; + af3 = absl::nullopt; + + OS aa4; + OS ab4 = absl::nullopt; + OS ac4 = std::string(n, 'c'); + std::string sad4(n, 'd'); + OS ad4 = sad4; + OS ae4(absl::in_place, n, 'e'); + OS af4; + af4.emplace(n, 'f'); + + aa4 = OS(absl::in_place, n, 'a'); + ab4 = OS(absl::in_place, n, 'b'); + ac4 = OS(absl::in_place, n, 'c'); + ad4 = OS(absl::in_place, n, 'd'); + ae4 = OS(absl::in_place, n, 'e'); + af4 = OS(absl::in_place, n, 'f'); + + OS aa5; + OS ab5 = absl::nullopt; + OS ac5 = std::string(n, 'c'); + std::string sad5(n, 'd'); + OS ad5 = sad5; + OS ae5(absl::in_place, n, 'e'); + OS af5; + af5.emplace(n, 'f'); + + std::string saa5(n, 'a'); + std::string sab5(n, 'a'); + std::string sac5(n, 'a'); + std::string sad52(n, 'a'); + std::string sae5(n, 'a'); + std::string saf5(n, 'a'); + + aa5 = saa5; + ab5 = sab5; + ac5 = sac5; + ad5 = sad52; + ae5 = sae5; + af5 = saf5; + + OS aa6; + OS ab6 = absl::nullopt; + OS ac6 = std::string(n, 'c'); + std::string sad6(n, 'd'); + OS ad6 = sad6; + OS ae6(absl::in_place, n, 'e'); + OS af6; + af6.emplace(n, 'f'); + + aa6 = std::string(n, 'a'); + ab6 = std::string(n, 'b'); + ac6 = std::string(n, 'c'); + ad6 = std::string(n, 'd'); + ae6 = std::string(n, 'e'); + af6 = std::string(n, 'f'); + + OS aa7; + OS ab7 = absl::nullopt; + OS ac7 = std::string(n, 'c'); + std::string sad7(n, 'd'); + OS ad7 = sad7; + OS ae7(absl::in_place, n, 'e'); + OS af7; + af7.emplace(n, 'f'); + + aa7.emplace(n, 'A'); + ab7.emplace(n, 'B'); + ac7.emplace(n, 'C'); + ad7.emplace(n, 'D'); + ae7.emplace(n, 'E'); + af7.emplace(n, 'F'); +} + +TEST(optionalTest, MoveAssignRegression) { + StructorListener listener; + Listenable::listener = &listener; + + { + absl::optional a; + Listenable b; + a = std::move(b); + } + + EXPECT_EQ(1, listener.construct0); + EXPECT_EQ(1, listener.move); + EXPECT_EQ(2, listener.destruct); +} + +TEST(optionalTest, ValueType) { + EXPECT_TRUE((std::is_same::value_type, int>::value)); + EXPECT_TRUE( + (std::is_same::value_type, std::string>::value)); + EXPECT_FALSE( + (std::is_same::value_type, absl::nullopt_t>::value)); +} + +TEST(optionalTest, Hash) { + std::hash> hash; + std::set hashcodes; + hashcodes.insert(hash(absl::nullopt)); + for (int i = 0; i < 100; ++i) { + hashcodes.insert(hash(i)); + } + EXPECT_GT(hashcodes.size(), 90); +} + +struct MoveMeNoThrow { + MoveMeNoThrow() : x(0) {} + [[noreturn]] MoveMeNoThrow(const MoveMeNoThrow& other) : x(other.x) { + ABSL_RAW_LOG(FATAL, "Should not be called."); + abort(); + } + MoveMeNoThrow(MoveMeNoThrow&& other) noexcept : x(other.x) {} + int x; +}; + +struct MoveMeThrow { + MoveMeThrow() : x(0) {} + MoveMeThrow(const MoveMeThrow& other) : x(other.x) {} + MoveMeThrow(MoveMeThrow&& other) : x(other.x) {} + int x; +}; + +TEST(optionalTest, NoExcept) { + static_assert( + std::is_nothrow_move_constructible>::value, + ""); +#ifndef ABSL_HAVE_STD_OPTIONAL + static_assert(absl::default_allocator_is_nothrow::value == + std::is_nothrow_move_constructible< + absl::optional>::value, + ""); +#endif + std::vector> v; + for (int i = 0; i < 10; ++i) v.emplace_back(); +} + +struct AnyLike { + AnyLike(AnyLike&&) = default; + AnyLike(const AnyLike&) = default; + + template ::type, + typename std::enable_if< + !absl::disjunction< + std::is_same, + absl::negation>>::value, + int>::type = 0> + AnyLike(ValueType&&) {} // NOLINT(runtime/explicit) + + AnyLike& operator=(AnyLike&&) = default; + AnyLike& operator=(const AnyLike&) = default; + + template ::type> + typename std::enable_if< + absl::conjunction>, + std::is_copy_constructible>::value, + AnyLike&>::type + operator=(ValueType&& /* rhs */) { + return *this; + } +}; + +TEST(optionalTest, ConstructionConstraints) { + EXPECT_TRUE((std::is_constructible>::value)); + + EXPECT_TRUE( + (std::is_constructible&>::value)); + + EXPECT_TRUE((std::is_constructible, AnyLike>::value)); + EXPECT_TRUE( + (std::is_constructible, const AnyLike&>::value)); + + EXPECT_TRUE((std::is_convertible, AnyLike>::value)); + + EXPECT_TRUE( + (std::is_convertible&, AnyLike>::value)); + + EXPECT_TRUE((std::is_convertible>::value)); + EXPECT_TRUE( + (std::is_convertible>::value)); + + EXPECT_TRUE(std::is_move_constructible>::value); + EXPECT_TRUE(std::is_copy_constructible>::value); +} + +TEST(optionalTest, AssignmentConstraints) { + EXPECT_TRUE((std::is_assignable>::value)); + EXPECT_TRUE( + (std::is_assignable&>::value)); + EXPECT_TRUE((std::is_assignable&, AnyLike>::value)); + EXPECT_TRUE( + (std::is_assignable&, const AnyLike&>::value)); + EXPECT_TRUE(std::is_move_assignable>::value); + EXPECT_TRUE(std::is_copy_assignable>::value); +} + +} // namespace diff --git a/absl/types/span.h b/absl/types/span.h new file mode 100644 index 00000000..0e26fd4d --- /dev/null +++ b/absl/types/span.h @@ -0,0 +1,738 @@ +// +// 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. +// +// ----------------------------------------------------------------------------- +// span.h +// ----------------------------------------------------------------------------- +// +// This header file defines a `Span` type for holding a view of an existing +// array of data. The `Span` object, much like the `absl::string_view` object, +// does not own such data itself. A span provides a lightweight way to pass +// around view of such data. +// +// Additionally, this header file defines `MakeSpan()` and `MakeConstSpan()` +// factory functions, for clearly creating spans of type `Span` or read-only +// `Span` when such types may be difficult to identify due to issues +// with implicit conversion. +// +// The C++ standards committee currently has a proposal for a `std::span` type, +// (http://wg21.link/p0122), which is not yet part of the standard (though may +// become part of C++20). As of August 2017, the differences between +// `absl::Span` and this proposal are: +// * `absl::Span` uses `size_t` for `size_type` +// * `absl::Span` has no `operator()` +// * `absl::Span` has no constructors for `std::unique_ptr` or +// `std::shared_ptr` +// * `absl::span` has the factory functions `MakeSpan()` and +// `MakeConstSpan()` +// * `absl::Span` has `front()` and `back()` methods +// * bounds-checked access to `absl::Span` is accomplished with `at()` +// * `absl::Span` has compiler-provided move and copy constructors and +// assignment. This is due to them being specified as `constexpr`, but that +// implies const in C++11. +// * `absl::Span` has no `element_type` or `index_type` typedefs +// * A read-only `absl::Span` can be implicitly constructed from an +// initializer list. +// * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or +// `as_mutable_bytes()` methods +// * `absl::Span` has no static extent template parameter, nor constructors +// which exist only because of the static extent parameter. +// * `absl::Span` has an explicit mutable-reference constructor +// +// For more information, see the class comments below. +#ifndef ABSL_TYPES_SPAN_H_ +#define ABSL_TYPES_SPAN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/meta/type_traits.h" + +namespace absl { + +template +class Span; + +namespace span_internal { +// A constexpr min function +constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; } + +// Wrappers for access to container data pointers. +template +constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references) + -> decltype(c.data()) { + return c.data(); +} + +// Before C++17, std::string::data returns a const char* in all cases. +inline char* GetDataImpl(std::string& s, // NOLINT(runtime/references) + int) noexcept { + return &s[0]; +} + +template +constexpr auto GetData(C& c) noexcept // NOLINT(runtime/references) + -> decltype(GetDataImpl(c, 0)) { + return GetDataImpl(c, 0); +} + +// Detection idioms for size() and data(). +template +using HasSize = + std::is_integral().size())>>; + +// We want to enable conversion from vector to Span but +// disable conversion from vector to Span. Here we use +// the fact that U** is convertible to Q* const* if and only if Q is the same +// type or a more cv-qualified version of U. We also decay the result type of +// data() to avoid problems with classes which have a member function data() +// which returns a reference. +template +using HasData = + std::is_convertible()))>*, + T* const*>; + +// Extracts value type from a Container +template +struct ElementType { + using type = typename absl::remove_reference_t::value_type; +}; + +template +struct ElementType { + using type = T; +}; + +template +using ElementT = typename ElementType::type; + +template +using EnableIfMutable = + typename std::enable_if::value, int>::type; + +template +bool EqualImpl(Span a, Span b) { + static_assert(std::is_const::value, ""); + return absl::equal(a.begin(), a.end(), b.begin(), b.end()); +} + +template +bool LessThanImpl(Span a, Span b) { + static_assert(std::is_const::value, ""); + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +// The `IsConvertible` classes here are needed because of the +// `std::is_convertible` bug in libcxx when compiled with GCC. This build +// configuration is used by Android NDK toolchain. Reference link: +// https://bugs.llvm.org/show_bug.cgi?id=27538. +template +struct IsConvertibleHelper { + private: + static std::true_type test(To); + static std::false_type test(...); + + public: + using type = decltype(test(std::declval())); +}; + +template +struct IsConvertible : IsConvertibleHelper::type {}; + +// TODO(zhangxy): replace `IsConvertible` with `std::is_convertible` once the +// older version of libcxx is not supported. +template +using EnableIfConvertibleToSpanConst = + typename std::enable_if>::value>::type; +} // namespace span_internal + +//------------------------------------------------------------------------------ +// Span +//------------------------------------------------------------------------------ +// +// A `Span` is an "array view" type for holding a view of a contiguous data +// array; the `Span` object does not and cannot own such data itself. A span +// provides an easy way to provide overloads for anything operating on +// contiguous sequences without needing to manage pointers and array lengths +// manually. + +// A span is conceptually a pointer (ptr) and a length (size) into an already +// existing array of contiguous memory; the array it represents references the +// elements "ptr[0] .. ptr[size-1]". Passing a properly-constructed `Span` +// instead of raw pointers avoids many issues related to index out of bounds +// errors. +// +// Spans may also be constructed from containers holding contiguous sequences. +// Such containers must supply `data()` and `size() const` methods (e.g +// `std::vector`, `absl::InlinedVector`). All implicit conversions to +// `absl::Span` from such containers will create spans of type `const T`; +// spans which can mutate their values (of type `T`) must use explicit +// constructors. +// +// A `Span` is somewhat analogous to an `absl::string_view`, but for an array +// of elements of type `T`. A user of `Span` must ensure that the data being +// pointed to outlives the `Span` itself. +// +// You can construct a `Span` in several ways: +// +// * Explicitly from a reference to a container type +// * Explicitly from a pointer and size +// * Implicitly from a container type (but only for spans of type `const T`) +// * Using the `MakeSpan()` or `MakeConstSpan()` factory functions. +// +// Examples: +// +// // Construct a Span explicitly from a container: +// std::vector v = {1, 2, 3, 4, 5}; +// auto span = absl::Span(v); +// +// // Construct a Span explicitly from a C-style array: +// int a[5] = {1, 2, 3, 4, 5}; +// auto span = absl::Span(a); +// +// // Construct a Span implicitly from a container +// void MyRoutine(absl::Span a) { +// ... +// }; +// std::vector v = {1,2,3,4,5}; +// MyRoutine(v) // convert to Span +// +// Note that `Span` objects, in addition to requiring that the memory they +// point to remains alive, must also ensure that such memory does not get +// reallocated. Therefore, to avoid undefined behavior, containers with +// associated span views should not invoke operations that may reallocate memory +// (such as resizing) or invalidate iterarors into the container. +// +// One common use for a `Span` is when passing arguments to a routine that can +// accept a variety of array types (e.g. a `std::vector`, `absl::InlinedVector`, +// a C-style array, etc.). Instead of creating overloads for each case, you +// can simply specify a `Span` as the argument to such a routine. +// +// Example: +// +// void MyRoutine(absl::Span a) { +// ... +// }; +// +// std::vector v = {1,2,3,4,5}; +// MyRoutine(v); +// +// absl::InlinedVector my_inline_vector; +// MyRoutine(my_inline_vector); +// +// // Explicit constructor from pointer,size +// int* my_array = new int[10]; +// MyRoutine(absl::Span(my_array, 10)); +template +class Span { + private: + // Used to determine whether a Span can be constructed from a container of + // type C. + template + using EnableIfConvertibleFrom = + typename std::enable_if::value && + span_internal::HasSize::value>::type; + + // Used to SFINAE-enable a function when the slice elements are const. + template + using EnableIfConstView = + typename std::enable_if::value, U>::type; + + // Used to SFINAE-enable a function when the slice elements are mutable. + template + using EnableIfMutableView = + typename std::enable_if::value, U>::type; + + public: + using value_type = absl::remove_cv_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + + static const size_type npos = -1; + + constexpr Span() noexcept : Span(nullptr, 0) {} + constexpr Span(pointer array, size_type length) noexcept + : ptr_(array), len_(length) {} + + // Implicit conversion constructors + template + constexpr Span(T (&a)[N]) noexcept // NOLINT(runtime/explicit) + : Span(a, N) {} + + // Explicit reference constructor for a mutable `Span` type + template , + typename = EnableIfMutableView> + explicit Span(V& v) noexcept // NOLINT(runtime/references) + : Span(span_internal::GetData(v), v.size()) {} + + // Implicit reference constructor for a read-only `Span` type + template , + typename = EnableIfConstView> + constexpr Span(const V& v) noexcept // NOLINT(runtime/explicit) + : Span(span_internal::GetData(v), v.size()) {} + + // Implicit constructor from an initializer list, making it possible to pass a + // brace-enclosed initializer list to a function expecting a `Span`. Such + // spans constructed from an initializer list must be of type `Span`. + // + // void Process(absl::Span x); + // Process({1, 2, 3}); + // + // Note that as always the array referenced by the span must outlive the span. + // Since an initializer list constructor acts as if it is fed a temporary + // array (cf. C++ standard [dcl.init.list]/5), it's safe to use this + // constructor only when the `std::initializer_list` itself outlives the span. + // In order to meet this requirement it's sufficient to ensure that neither + // the span nor a copy of it is used outside of the expression in which it's + // created: + // + // // Assume that this function uses the array directly, not retaining any + // // copy of the span or pointer to any of its elements. + // void Process(absl::Span ints); + // + // // Okay: the std::initializer_list will reference a temporary array + // // that isn't destroyed until after the call to Process returns. + // Process({ 17, 19 }); + // + // // Not okay: the storage used by the std::initializer_list is not + // // allowed to be referenced after the first line. + // absl::Span ints = { 17, 19 }; + // Process(ints); + // + // // Not okay for the same reason as above: even when the elements of the + // // initializer list expression are not temporaries the underlying array + // // is, so the initializer list must still outlive the span. + // const int foo = 17; + // absl::Span ints = { foo }; + // Process(ints); + // + template > + Span( + std::initializer_list v) noexcept // NOLINT(runtime/explicit) + : Span(v.begin(), v.size()) {} + + // Accessors + + // Span::data() + // + // Returns a pointer to the span's underlying array of data (which is held + // outside the span). + constexpr pointer data() const noexcept { return ptr_; } + + // Span::size() + // + // Returns the size of this span. + constexpr size_type size() const noexcept { return len_; } + + // Span::length() + // + // Returns the length (size) of this span. + constexpr size_type length() const noexcept { return size(); } + + // Span::empty() + // + // Returns a boolean indicating whether or not this span is considered empty. + constexpr bool empty() const noexcept { return size() == 0; } + + // Span::operator[] + // + // Returns a reference to the i'th element of this span. + constexpr reference operator[](size_type i) const noexcept { + // MSVC 2015 accepts this as constexpr, but not ptr_[i] + return *(data() + i); + } + + // Span::at() + // + // Returns a reference to the i'th element of this span. + constexpr reference at(size_type i) const { + return ABSL_PREDICT_FALSE(i < size()) + ? ptr_[i] + : (base_internal::ThrowStdOutOfRange( + "Span::at failed bounds check"), + ptr_[i]); + } + + // Span::front() + // + // Returns a reference to the first element of this span. + reference front() const noexcept { return ABSL_ASSERT(size() > 0), ptr_[0]; } + + // Span::back() + // + // Returns a reference to the last element of this span. + reference back() const noexcept { + return ABSL_ASSERT(size() > 0), ptr_[size() - 1]; + } + + // Span::begin() + // + // Returns an iterator to the first element of this span. + constexpr iterator begin() const noexcept { return ptr_; } + + // Span::cbegin() + // + // Returns a const iterator to the first element of this span. + constexpr const_iterator cbegin() const noexcept { return ptr_; } + + // Span::end() + // + // Returns an iterator to the last element of this span. + iterator end() const noexcept { return ptr_ + len_; } + + // Span::cend() + // + // Returns a const iterator to the last element of this span. + const_iterator cend() const noexcept { return end(); } + + // Span::rbegin() + // + // Returns a reverse iterator starting at the last element of this span. + reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } + + // Span::crbegin() + // + // Returns a reverse const iterator starting at the last element of this span. + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + + // Span::rend() + // + // Returns a reverse iterator starting at the first element of this span. + reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } + + // Span::crend() + // + // Returns a reverse iterator starting at the first element of this span. + const_reverse_iterator crend() const noexcept { return rend(); } + + // Span mutations + + // Span::remove_prefix() + // + // Removes the first `n` elements from the span. + void remove_prefix(size_type n) noexcept { + assert(len_ >= n); + ptr_ += n; + len_ -= n; + } + + // Span::remove_suffix() + // + // Removes the last `n` elements from the span. + void remove_suffix(size_type n) noexcept { + assert(len_ >= n); + len_ -= n; + } + + // Span::subspan() + // + // Returns a `Span` starting at element `pos` and of length `len`, with + // proper bounds checking to ensure `len` does not exceed the ptr+size of the + // original array. (Spans whose `len` would point past the end of the array + // will throw a `std::out_of_range`.) + constexpr Span subspan(size_type pos = 0, size_type len = npos) const { + return (pos <= len_) + ? Span(ptr_ + pos, span_internal::Min(len_ - pos, len)) + : (base_internal::ThrowStdOutOfRange("pos > size()"), Span()); + } + + private: + pointer ptr_; + size_type len_; +}; + +template +const typename Span::size_type Span::npos; + +// Span relationals + +// Equality is compared element-by-element, while ordering is lexicographical. +// We provide three overloads for each operator to cover any combination on the +// left or right hand side of mutable Span, read-only Span, and +// convertible-to-read-only Span. +// TODO(zhangxy): Due to MSVC overload resolution bug with partial ordering +// template functions, 5 overloads per operator is needed as a workaround. We +// should update them to 3 overloads per operator using non-deduced context like +// string_view, i.e. +// - (Span, Span) +// - (Span, non_deduced>) +// - (non_deduced>, Span) + +// operator== +template +bool operator==(Span a, Span b) { + return span_internal::EqualImpl(a, b); +} +template +bool operator==(Span a, Span b) { + return span_internal::EqualImpl(a, b); +} +template +bool operator==(Span a, Span b) { + return span_internal::EqualImpl(a, b); +} +template > +bool operator==(const U& a, Span b) { + return span_internal::EqualImpl(a, b); +} +template > +bool operator==(Span a, const U& b) { + return span_internal::EqualImpl(a, b); +} + +// operator!= +template +bool operator!=(Span a, Span b) { + return !(a == b); +} +template +bool operator!=(Span a, Span b) { + return !(a == b); +} +template +bool operator!=(Span a, Span b) { + return !(a == b); +} +template > +bool operator!=(const U& a, Span b) { + return !(a == b); +} +template > +bool operator!=(Span a, const U& b) { + return !(a == b); +} + +// operator< +template +bool operator<(Span a, Span b) { + return span_internal::LessThanImpl(a, b); +} +template +bool operator<(Span a, Span b) { + return span_internal::LessThanImpl(a, b); +} +template +bool operator<(Span a, Span b) { + return span_internal::LessThanImpl(a, b); +} +template > +bool operator<(const U& a, Span b) { + return span_internal::LessThanImpl(a, b); +} +template > +bool operator<(Span a, const U& b) { + return span_internal::LessThanImpl(a, b); +} + +// operator> +template +bool operator>(Span a, Span b) { + return b < a; +} +template +bool operator>(Span a, Span b) { + return b < a; +} +template +bool operator>(Span a, Span b) { + return b < a; +} +template > +bool operator>(const U& a, Span b) { + return b < a; +} +template > +bool operator>(Span a, const U& b) { + return b < a; +} + +// operator<= +template +bool operator<=(Span a, Span b) { + return !(b < a); +} +template +bool operator<=(Span a, Span b) { + return !(b < a); +} +template +bool operator<=(Span a, Span b) { + return !(b < a); +} +template > +bool operator<=(const U& a, Span b) { + return !(b < a); +} +template > +bool operator<=(Span a, const U& b) { + return !(b < a); +} + +// operator>= +template +bool operator>=(Span a, Span b) { + return !(a < b); +} +template +bool operator>=(Span a, Span b) { + return !(a < b); +} +template +bool operator>=(Span a, Span b) { + return !(a < b); +} +template > +bool operator>=(const U& a, Span b) { + return !(a < b); +} +template > +bool operator>=(Span a, const U& b) { + return !(a < b); +} + +// MakeSpan() +// +// Constructs a mutable `Span`, deducing `T` automatically from either a +// container or pointer+size. +// +// Because a read-only `Span` is implicitly constructed from container +// types regardless of whether the container itself is a const container, +// constructing mutable spans of type `Span` from containers requires +// explicit constructors. The container-accepting version of `MakeSpan()` +// deduces the type of `T` by the constness of the pointer received from the +// container's `data()` member. Similarly, the pointer-accepting version returns +// a `Span` if `T` is `const`, and a `Span` otherwise. +// +// Examples: +// +// void MyRoutine(absl::Span a) { +// ... +// }; +// // my_vector is a container of non-const types +// std::vector my_vector; +// +// // Constructing a Span implicitly attempts to create a Span of type +// // `Span` +// MyRoutine(my_vector); // error, type mismatch +// +// // Explicitly constructing the Span is verbose +// MyRoutine(absl::Span(my_vector); +// +// // Use MakeSpan() to make an absl::Span +// MyRoutine(absl::MakeSpan(my_vector)); +// +// // Construct a span from an array ptr+size +// absl::Span my_span() { +// return absl::MakeSpan(&array[0], num_elements_); +// } +// +template +constexpr Span MakeSpan(T* ptr, size_t size) noexcept { + return Span(ptr, size); +} + +template +Span MakeSpan(T* begin, T* end) noexcept { + return ABSL_ASSERT(begin <= end), Span(begin, end - begin); +} + +template +constexpr auto MakeSpan(C& c) noexcept // NOLINT(runtime/references) + -> decltype(absl::MakeSpan(span_internal::GetData(c), c.size())) { + return MakeSpan(span_internal::GetData(c), c.size()); +} + +template +constexpr Span MakeSpan(T (&array)[N]) noexcept { + return Span(array, N); +} + +// MakeConstSpan() +// +// Constructs a `Span` as with `MakeSpan`, deducing `T` automatically, +// but always returning a `Span`. +// +// Examples: +// +// void ProcessInts(absl::Span some_ints); +// +// // Call with a pointer and size. +// int array[3] = { 0, 0, 0 }; +// ProcessInts(absl::MakeConstSpan(&array[0], 3)); +// +// // Call with a [begin, end) pair. +// ProcessInts(absl::MakeConstSpan(&array[0], &array[3])); +// +// // Call directly with an array. +// ProcessInts(absl::MakeConstSpan(array)); +// +// // Call with a contiguous container. +// std::vector some_ints = ...; +// ProcessInts(absl::MakeConstSpan(some_ints)); +// ProcessInts(absl::MakeConstSpan(std::vector{ 0, 0, 0 })); +// +template +constexpr Span MakeConstSpan(T* ptr, size_t size) noexcept { + return Span(ptr, size); +} + +template +Span MakeConstSpan(T* begin, T* end) noexcept { + return ABSL_ASSERT(begin <= end), Span(begin, end - begin); +} + +template +constexpr auto MakeConstSpan(const C& c) noexcept -> decltype(MakeSpan(c)) { + return MakeSpan(c); +} + +template +constexpr Span MakeConstSpan(const T (&array)[N]) noexcept { + return Span(array, N); +} +} // namespace absl +#endif // ABSL_TYPES_SPAN_H_ diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc new file mode 100644 index 00000000..22ea33e0 --- /dev/null +++ b/absl/types/span_test.cc @@ -0,0 +1,783 @@ +// 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. + +#include "absl/types/span.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/exception_testing.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/container/fixed_array.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/str_cat.h" + +namespace { + +MATCHER_P(DataIs, data, + absl::StrCat("data() is ", negation ? "is " : "isn't ", + testing::PrintToString(data))) { + return arg.data() == data; +} + +template +auto SpanIs(T data, size_t size) + -> decltype(testing::AllOf(DataIs(data), testing::SizeIs(size))) { + return testing::AllOf(DataIs(data), testing::SizeIs(size)); +} + +template +auto SpanIs(const Container& c) -> decltype(SpanIs(c.data(), c.size())) { + return SpanIs(c.data(), c.size()); +} + +std::vector MakeRamp(int len, int offset = 0) { + std::vector v(len); + std::iota(v.begin(), v.end(), offset); + return v; +} + +TEST(IntSpan, EmptyCtors) { + absl::Span s; + EXPECT_THAT(s, SpanIs(nullptr, 0)); +} + +TEST(IntSpan, PtrLenCtor) { + int a[] = {1, 2, 3}; + absl::Span s(&a[0], 2); + EXPECT_THAT(s, SpanIs(a, 2)); +} + +TEST(IntSpan, ArrayCtor) { + int a[] = {1, 2, 3}; + absl::Span s(a); + EXPECT_THAT(s, SpanIs(a, 3)); + + EXPECT_TRUE((std::is_constructible, int[3]>::value)); + EXPECT_TRUE( + (std::is_constructible, const int[3]>::value)); + EXPECT_FALSE((std::is_constructible, const int[3]>::value)); + EXPECT_TRUE((std::is_convertible>::value)); + EXPECT_TRUE( + (std::is_convertible>::value)); +} + +template +void TakesGenericSpan(absl::Span) {} + +TEST(IntSpan, ContainerCtor) { + std::vector empty; + absl::Span s_empty(empty); + EXPECT_THAT(s_empty, SpanIs(empty)); + + std::vector filled{1, 2, 3}; + absl::Span s_filled(filled); + EXPECT_THAT(s_filled, SpanIs(filled)); + + absl::Span s_from_span(filled); + EXPECT_THAT(s_from_span, SpanIs(s_filled)); + + absl::Span const_filled = filled; + EXPECT_THAT(const_filled, SpanIs(filled)); + + absl::Span const_from_span = s_filled; + EXPECT_THAT(const_from_span, SpanIs(s_filled)); + + EXPECT_TRUE( + (std::is_convertible&, absl::Span>::value)); + EXPECT_TRUE( + (std::is_convertible&, absl::Span>::value)); + + TakesGenericSpan(absl::Span(filled)); +} + +// A struct supplying shallow data() const. +struct ContainerWithShallowConstData { + std::vector storage; + int* data() const { return const_cast(storage.data()); } + int size() const { return storage.size(); } +}; + +TEST(IntSpan, ShallowConstness) { + const ContainerWithShallowConstData c{MakeRamp(20)}; + absl::Span s( + c); // We should be able to do this even though data() is const. + s[0] = -1; + EXPECT_EQ(c.storage[0], -1); +} + +TEST(CharSpan, StringCtor) { + std::string empty = ""; + absl::Span s_empty(empty); + EXPECT_THAT(s_empty, SpanIs(empty)); + + std::string abc = "abc"; + absl::Span s_abc(abc); + EXPECT_THAT(s_abc, SpanIs(abc)); + + absl::Span s_const_abc = abc; + EXPECT_THAT(s_const_abc, SpanIs(abc)); + + EXPECT_FALSE((std::is_constructible, std::string>::value)); + EXPECT_FALSE((std::is_constructible, std::string>::value)); + EXPECT_TRUE((std::is_convertible>::value)); +} + +TEST(IntSpan, FromConstPointer) { + EXPECT_TRUE((std::is_constructible, + std::vector>::value)); + EXPECT_TRUE((std::is_constructible, + std::vector>::value)); + EXPECT_FALSE(( + std::is_constructible, std::vector>::value)); + EXPECT_FALSE(( + std::is_constructible, std::vector>::value)); +} + +struct TypeWithMisleadingData { + int& data() { return i; } + int size() { return 1; } + int i; +}; + +struct TypeWithMisleadingSize { + int* data() { return &i; } + const char* size() { return "1"; } + int i; +}; + +TEST(IntSpan, EvilTypes) { + EXPECT_FALSE( + (std::is_constructible, TypeWithMisleadingData&>::value)); + EXPECT_FALSE( + (std::is_constructible, TypeWithMisleadingSize&>::value)); +} + +struct Base { + int* data() { return &i; } + int size() { return 1; } + int i; +}; +struct Derived : Base {}; + +TEST(IntSpan, SpanOfDerived) { + EXPECT_TRUE((std::is_constructible, Base&>::value)); + EXPECT_TRUE((std::is_constructible, Derived&>::value)); + EXPECT_FALSE( + (std::is_constructible, std::vector>::value)); +} + +void TestInitializerList(absl::Span s, const std::vector& v) { + EXPECT_TRUE(absl::equal(s.begin(), s.end(), v.begin(), v.end())); +} + +TEST(ConstIntSpan, InitializerListConversion) { + TestInitializerList({}, {}); + TestInitializerList({1}, {1}); + TestInitializerList({1, 2, 3}, {1, 2, 3}); + + EXPECT_FALSE((std::is_constructible, + std::initializer_list>::value)); + EXPECT_FALSE(( + std::is_convertible, std::initializer_list>::value)); +} + +TEST(IntSpan, Data) { + int i; + absl::Span s(&i, 1); + EXPECT_EQ(&i, s.data()); +} + +TEST(IntSpan, SizeLengthEmpty) { + absl::Span empty; + EXPECT_EQ(empty.size(), 0); + EXPECT_TRUE(empty.empty()); + EXPECT_EQ(empty.size(), empty.length()); + + auto v = MakeRamp(10); + absl::Span s(v); + EXPECT_EQ(s.size(), 10); + EXPECT_FALSE(s.empty()); + EXPECT_EQ(s.size(), s.length()); +} + +TEST(IntSpan, ElementAccess) { + auto v = MakeRamp(10); + absl::Span s(v); + for (int i = 0; i < s.size(); ++i) { + EXPECT_EQ(s[i], s.at(i)); + } + + EXPECT_EQ(s.front(), s[0]); + EXPECT_EQ(s.back(), s[9]); +} + +TEST(IntSpan, AtThrows) { + auto v = MakeRamp(10); + absl::Span s(v); + + EXPECT_EQ(s.at(9), 9); + ABSL_BASE_INTERNAL_EXPECT_FAIL(s.at(10), std::out_of_range, + "failed bounds check"); +} + +TEST(IntSpan, RemovePrefixAndSuffix) { + auto v = MakeRamp(20, 1); + absl::Span s(v); + EXPECT_EQ(s.size(), 20); + + s.remove_suffix(0); + s.remove_prefix(0); + EXPECT_EQ(s.size(), 20); + + s.remove_prefix(1); + EXPECT_EQ(s.size(), 19); + EXPECT_EQ(s[0], 2); + + s.remove_suffix(1); + EXPECT_EQ(s.size(), 18); + EXPECT_EQ(s.back(), 19); + + s.remove_prefix(7); + EXPECT_EQ(s.size(), 11); + EXPECT_EQ(s[0], 9); + + s.remove_suffix(11); + EXPECT_EQ(s.size(), 0); + + EXPECT_EQ(v, MakeRamp(20, 1)); +} + +TEST(IntSpan, Subspan) { + std::vector empty; + EXPECT_EQ(absl::MakeSpan(empty).subspan(), empty); + EXPECT_THAT(absl::MakeSpan(empty).subspan(0, 0), SpanIs(empty)); + EXPECT_THAT(absl::MakeSpan(empty).subspan(0, absl::Span::npos), + SpanIs(empty)); + + auto ramp = MakeRamp(10); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(), SpanIs(ramp)); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(0, 10), SpanIs(ramp)); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(0, absl::Span::npos), + SpanIs(ramp)); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(0, 3), SpanIs(ramp.data(), 3)); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(5, absl::Span::npos), + SpanIs(ramp.data() + 5, 5)); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(3, 3), SpanIs(ramp.data() + 3, 3)); + EXPECT_THAT(absl::MakeSpan(ramp).subspan(10, 5), SpanIs(ramp.data() + 10, 0)); + +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(absl::MakeSpan(ramp).subspan(11, 5), std::out_of_range); +#else + EXPECT_DEATH(absl::MakeSpan(ramp).subspan(11, 5), ""); +#endif +} + +TEST(IntSpan, MakeSpanPtrLength) { + std::vector empty; + auto s_empty = absl::MakeSpan(empty.data(), empty.size()); + EXPECT_THAT(s_empty, SpanIs(empty)); + + std::array a{{1, 2, 3}}; + auto s = absl::MakeSpan(a.data(), a.size()); + EXPECT_THAT(s, SpanIs(a)); + + EXPECT_THAT(absl::MakeConstSpan(empty.data(), empty.size()), SpanIs(s_empty)); + EXPECT_THAT(absl::MakeConstSpan(a.data(), a.size()), SpanIs(s)); +} + +TEST(IntSpan, MakeSpanTwoPtrs) { + std::vector empty; + auto s_empty = absl::MakeSpan(empty.data(), empty.data()); + EXPECT_THAT(s_empty, SpanIs(empty)); + + std::vector v{1, 2, 3}; + auto s = absl::MakeSpan(v.data(), v.data() + 1); + EXPECT_THAT(s, SpanIs(v.data(), 1)); + + EXPECT_THAT(absl::MakeConstSpan(empty.data(), empty.data()), SpanIs(s_empty)); + EXPECT_THAT(absl::MakeConstSpan(v.data(), v.data() + 1), SpanIs(s)); +} + +TEST(IntSpan, MakeSpanContainer) { + std::vector empty; + auto s_empty = absl::MakeSpan(empty); + EXPECT_THAT(s_empty, SpanIs(empty)); + + std::vector v{1, 2, 3}; + auto s = absl::MakeSpan(v); + EXPECT_THAT(s, SpanIs(v)); + + EXPECT_THAT(absl::MakeConstSpan(empty), SpanIs(s_empty)); + EXPECT_THAT(absl::MakeConstSpan(v), SpanIs(s)); + + EXPECT_THAT(absl::MakeSpan(s), SpanIs(s)); + EXPECT_THAT(absl::MakeConstSpan(s), SpanIs(s)); +} + +TEST(CharSpan, MakeSpanString) { + std::string empty = ""; + auto s_empty = absl::MakeSpan(empty); + EXPECT_THAT(s_empty, SpanIs(empty)); + + std::string str = "abc"; + auto s_str = absl::MakeSpan(str); + EXPECT_THAT(s_str, SpanIs(str)); + + EXPECT_THAT(absl::MakeConstSpan(empty), SpanIs(s_empty)); + EXPECT_THAT(absl::MakeConstSpan(str), SpanIs(s_str)); +} + +TEST(IntSpan, MakeSpanArray) { + int a[] = {1, 2, 3}; + auto s = absl::MakeSpan(a); + EXPECT_THAT(s, SpanIs(a, 3)); + + const int ca[] = {1, 2, 3}; + auto s_ca = absl::MakeSpan(ca); + EXPECT_THAT(s_ca, SpanIs(ca, 3)); + + EXPECT_THAT(absl::MakeConstSpan(a), SpanIs(s)); + EXPECT_THAT(absl::MakeConstSpan(ca), SpanIs(s_ca)); +} + +// Compile-asserts that the argument has the expected decayed type. +template +void CheckType(const T& /* value */) { + testing::StaticAssertTypeEq(); +} + +TEST(IntSpan, MakeSpanTypes) { + std::vector vec; + const std::vector cvec; + int a[1]; + const int ca[] = {1}; + int* ip = a; + const int* cip = ca; + std::string s = ""; + const std::string cs = ""; + CheckType>(absl::MakeSpan(vec)); + CheckType>(absl::MakeSpan(cvec)); + CheckType>(absl::MakeSpan(ip, ip + 1)); + CheckType>(absl::MakeSpan(ip, 1)); + CheckType>(absl::MakeSpan(cip, cip + 1)); + CheckType>(absl::MakeSpan(cip, 1)); + CheckType>(absl::MakeSpan(a)); + CheckType>(absl::MakeSpan(a, a + 1)); + CheckType>(absl::MakeSpan(a, 1)); + CheckType>(absl::MakeSpan(ca)); + CheckType>(absl::MakeSpan(ca, ca + 1)); + CheckType>(absl::MakeSpan(ca, 1)); + CheckType>(absl::MakeSpan(s)); + CheckType>(absl::MakeSpan(cs)); +} + +TEST(ConstIntSpan, MakeConstSpanTypes) { + std::vector vec; + const std::vector cvec; + int array[1]; + const int carray[] = {0}; + int* ptr = array; + const int* cptr = carray; + std::string s = ""; + std::string cs = ""; + CheckType>(absl::MakeConstSpan(vec)); + CheckType>(absl::MakeConstSpan(cvec)); + CheckType>(absl::MakeConstSpan(ptr, ptr + 1)); + CheckType>(absl::MakeConstSpan(ptr, 1)); + CheckType>(absl::MakeConstSpan(cptr, cptr + 1)); + CheckType>(absl::MakeConstSpan(cptr, 1)); + CheckType>(absl::MakeConstSpan(array)); + CheckType>(absl::MakeConstSpan(carray)); + CheckType>(absl::MakeConstSpan(s)); + CheckType>(absl::MakeConstSpan(cs)); +} + +TEST(IntSpan, Equality) { + const int arr1[] = {1, 2, 3, 4, 5}; + int arr2[] = {1, 2, 3, 4, 5}; + std::vector vec1(std::begin(arr1), std::end(arr1)); + std::vector vec2 = vec1; + std::vector other_vec = {2, 4, 6, 8, 10}; + // These two slices are from different vectors, but have the same size and + // have the same elements (right now). They should compare equal. Test both + // == and !=. + const absl::Span from1 = vec1; + const absl::Span from2 = vec2; + EXPECT_EQ(from1, from1); + EXPECT_FALSE(from1 != from1); + EXPECT_EQ(from1, from2); + EXPECT_FALSE(from1 != from2); + + // These two slices have different underlying vector values. They should be + // considered not equal. Test both == and !=. + const absl::Span from_other = other_vec; + EXPECT_NE(from1, from_other); + EXPECT_FALSE(from1 == from_other); + + // Comparison between a vector and its slice should be equal. And vice-versa. + // This ensures implicit conversion to Span works on both sides of ==. + EXPECT_EQ(vec1, from1); + EXPECT_FALSE(vec1 != from1); + EXPECT_EQ(from1, vec1); + EXPECT_FALSE(from1 != vec1); + + // This verifies that absl::Span can be compared freely with + // absl::Span. + const absl::Span mutable_from1(vec1); + const absl::Span mutable_from2(vec2); + EXPECT_EQ(from1, mutable_from1); + EXPECT_EQ(mutable_from1, from1); + EXPECT_EQ(mutable_from1, mutable_from2); + EXPECT_EQ(mutable_from2, mutable_from1); + + // Comparison between a vector and its slice should be equal for mutable + // Spans as well. + EXPECT_EQ(vec1, mutable_from1); + EXPECT_FALSE(vec1 != mutable_from1); + EXPECT_EQ(mutable_from1, vec1); + EXPECT_FALSE(mutable_from1 != vec1); + + // Comparison between convertible-to-Span-of-const and Span-of-mutable. Arrays + // are used because they're the only value type which converts to a + // Span-of-mutable. EXPECT_TRUE is used instead of EXPECT_EQ to avoid + // array-to-pointer decay. + EXPECT_TRUE(arr1 == mutable_from1); + EXPECT_FALSE(arr1 != mutable_from1); + EXPECT_TRUE(mutable_from1 == arr1); + EXPECT_FALSE(mutable_from1 != arr1); + + // Comparison between convertible-to-Span-of-mutable and Span-of-const + EXPECT_TRUE(arr2 == from1); + EXPECT_FALSE(arr2 != from1); + EXPECT_TRUE(from1 == arr2); + EXPECT_FALSE(from1 != arr2); + + // With a different size, the array slices should not be equal. + EXPECT_NE(from1, absl::Span(from1).subspan(0, from1.size() - 1)); + + // With different contents, the array slices should not be equal. + ++vec2.back(); + EXPECT_NE(from1, from2); +} + +class IntSpanOrderComparisonTest : public testing::Test { + public: + IntSpanOrderComparisonTest() + : arr_before_{1, 2, 3}, + arr_after_{1, 2, 4}, + carr_after_{1, 2, 4}, + vec_before_(std::begin(arr_before_), std::end(arr_before_)), + vec_after_(std::begin(arr_after_), std::end(arr_after_)), + before_(vec_before_), + after_(vec_after_), + cbefore_(vec_before_), + cafter_(vec_after_) {} + + protected: + int arr_before_[3], arr_after_[3]; + const int carr_after_[3]; + std::vector vec_before_, vec_after_; + absl::Span before_, after_; + absl::Span cbefore_, cafter_; +}; + +TEST_F(IntSpanOrderComparisonTest, CompareSpans) { + EXPECT_TRUE(cbefore_ < cafter_); + EXPECT_TRUE(cbefore_ <= cafter_); + EXPECT_TRUE(cafter_ > cbefore_); + EXPECT_TRUE(cafter_ >= cbefore_); + + EXPECT_FALSE(cbefore_ > cafter_); + EXPECT_FALSE(cafter_ < cbefore_); + + EXPECT_TRUE(before_ < after_); + EXPECT_TRUE(before_ <= after_); + EXPECT_TRUE(after_ > before_); + EXPECT_TRUE(after_ >= before_); + + EXPECT_FALSE(before_ > after_); + EXPECT_FALSE(after_ < before_); + + EXPECT_TRUE(cbefore_ < after_); + EXPECT_TRUE(cbefore_ <= after_); + EXPECT_TRUE(after_ > cbefore_); + EXPECT_TRUE(after_ >= cbefore_); + + EXPECT_FALSE(cbefore_ > after_); + EXPECT_FALSE(after_ < cbefore_); +} + +TEST_F(IntSpanOrderComparisonTest, SpanOfConstAndContainer) { + EXPECT_TRUE(cbefore_ < vec_after_); + EXPECT_TRUE(cbefore_ <= vec_after_); + EXPECT_TRUE(vec_after_ > cbefore_); + EXPECT_TRUE(vec_after_ >= cbefore_); + + EXPECT_FALSE(cbefore_ > vec_after_); + EXPECT_FALSE(vec_after_ < cbefore_); + + EXPECT_TRUE(arr_before_ < cafter_); + EXPECT_TRUE(arr_before_ <= cafter_); + EXPECT_TRUE(cafter_ > arr_before_); + EXPECT_TRUE(cafter_ >= arr_before_); + + EXPECT_FALSE(arr_before_ > cafter_); + EXPECT_FALSE(cafter_ < arr_before_); +} + +TEST_F(IntSpanOrderComparisonTest, SpanOfMutableAndContainer) { + EXPECT_TRUE(vec_before_ < after_); + EXPECT_TRUE(vec_before_ <= after_); + EXPECT_TRUE(after_ > vec_before_); + EXPECT_TRUE(after_ >= vec_before_); + + EXPECT_FALSE(vec_before_ > after_); + EXPECT_FALSE(after_ < vec_before_); + + EXPECT_TRUE(before_ < carr_after_); + EXPECT_TRUE(before_ <= carr_after_); + EXPECT_TRUE(carr_after_ > before_); + EXPECT_TRUE(carr_after_ >= before_); + + EXPECT_FALSE(before_ > carr_after_); + EXPECT_FALSE(carr_after_ < before_); +} + +TEST_F(IntSpanOrderComparisonTest, EqualSpans) { + EXPECT_FALSE(before_ < before_); + EXPECT_TRUE(before_ <= before_); + EXPECT_FALSE(before_ > before_); + EXPECT_TRUE(before_ >= before_); +} + +TEST_F(IntSpanOrderComparisonTest, Subspans) { + auto subspan = before_.subspan(0, 1); + EXPECT_TRUE(subspan < before_); + EXPECT_TRUE(subspan <= before_); + EXPECT_TRUE(before_ > subspan); + EXPECT_TRUE(before_ >= subspan); + + EXPECT_FALSE(subspan > before_); + EXPECT_FALSE(before_ < subspan); +} + +TEST_F(IntSpanOrderComparisonTest, EmptySpans) { + absl::Span empty; + EXPECT_FALSE(empty < empty); + EXPECT_TRUE(empty <= empty); + EXPECT_FALSE(empty > empty); + EXPECT_TRUE(empty >= empty); + + EXPECT_TRUE(empty < before_); + EXPECT_TRUE(empty <= before_); + EXPECT_TRUE(before_ > empty); + EXPECT_TRUE(before_ >= empty); + + EXPECT_FALSE(empty > before_); + EXPECT_FALSE(before_ < empty); +} + +TEST(IntSpan, ExposesContainerTypesAndConsts) { + absl::Span slice; + CheckType::iterator>(slice.begin()); + EXPECT_TRUE((std::is_convertible::const_iterator>::value)); + CheckType::const_iterator>(slice.cbegin()); + EXPECT_TRUE((std::is_convertible::const_iterator>::value)); + CheckType::const_iterator>(slice.cend()); + CheckType::reverse_iterator>(slice.rend()); + EXPECT_TRUE( + (std::is_convertible::const_reverse_iterator>::value)); + CheckType::const_reverse_iterator>(slice.crend()); + testing::StaticAssertTypeEq::value_type>(); + testing::StaticAssertTypeEq::value_type>(); + testing::StaticAssertTypeEq::pointer>(); + testing::StaticAssertTypeEq::pointer>(); + testing::StaticAssertTypeEq::reference>(); + testing::StaticAssertTypeEq::reference>(); + testing::StaticAssertTypeEq::const_reference>(); + testing::StaticAssertTypeEq::const_reference>(); + EXPECT_EQ(static_cast::size_type>(-1), absl::Span::npos); +} + +TEST(IntSpan, IteratorsAndReferences) { + auto accept_pointer = [](int*) {}; + auto accept_reference = [](int&) {}; + auto accept_iterator = [](absl::Span::iterator) {}; + auto accept_const_iterator = [](absl::Span::const_iterator) {}; + auto accept_reverse_iterator = [](absl::Span::reverse_iterator) {}; + auto accept_const_reverse_iterator = + [](absl::Span::const_reverse_iterator) {}; + + int a[1]; + absl::Span s = a; + + accept_pointer(s.data()); + accept_iterator(s.begin()); + accept_const_iterator(s.begin()); + accept_const_iterator(s.cbegin()); + accept_iterator(s.end()); + accept_const_iterator(s.end()); + accept_const_iterator(s.cend()); + accept_reverse_iterator(s.rbegin()); + accept_const_reverse_iterator(s.rbegin()); + accept_const_reverse_iterator(s.crbegin()); + accept_reverse_iterator(s.rend()); + accept_const_reverse_iterator(s.rend()); + accept_const_reverse_iterator(s.crend()); + + accept_reference(s[0]); + accept_reference(s.at(0)); + accept_reference(s.front()); + accept_reference(s.back()); +} + +TEST(IntSpan, IteratorsAndReferences_Const) { + auto accept_pointer = [](int*) {}; + auto accept_reference = [](int&) {}; + auto accept_iterator = [](absl::Span::iterator) {}; + auto accept_const_iterator = [](absl::Span::const_iterator) {}; + auto accept_reverse_iterator = [](absl::Span::reverse_iterator) {}; + auto accept_const_reverse_iterator = + [](absl::Span::const_reverse_iterator) {}; + + int a[1]; + const absl::Span s = a; + + accept_pointer(s.data()); + accept_iterator(s.begin()); + accept_const_iterator(s.begin()); + accept_const_iterator(s.cbegin()); + accept_iterator(s.end()); + accept_const_iterator(s.end()); + accept_const_iterator(s.cend()); + accept_reverse_iterator(s.rbegin()); + accept_const_reverse_iterator(s.rbegin()); + accept_const_reverse_iterator(s.crbegin()); + accept_reverse_iterator(s.rend()); + accept_const_reverse_iterator(s.rend()); + accept_const_reverse_iterator(s.crend()); + + accept_reference(s[0]); + accept_reference(s.at(0)); + accept_reference(s.front()); + accept_reference(s.back()); +} + +TEST(IntSpan, NoexceptTest) { + int a[] = {1, 2, 3}; + std::vector v; + EXPECT_TRUE(noexcept(absl::Span())); + EXPECT_TRUE(noexcept(absl::Span(a, 2))); + EXPECT_TRUE(noexcept(absl::Span(a))); + EXPECT_TRUE(noexcept(absl::Span(v))); + EXPECT_TRUE(noexcept(absl::Span(v))); + EXPECT_TRUE(noexcept(absl::Span({1, 2, 3}))); + EXPECT_TRUE(noexcept(absl::MakeSpan(v))); + EXPECT_TRUE(noexcept(absl::MakeSpan(a))); + EXPECT_TRUE(noexcept(absl::MakeSpan(a, 2))); + EXPECT_TRUE(noexcept(absl::MakeSpan(a, a + 1))); + EXPECT_TRUE(noexcept(absl::MakeConstSpan(v))); + EXPECT_TRUE(noexcept(absl::MakeConstSpan(a))); + EXPECT_TRUE(noexcept(absl::MakeConstSpan(a, 2))); + EXPECT_TRUE(noexcept(absl::MakeConstSpan(a, a + 1))); + + absl::Span s(v); + EXPECT_TRUE(noexcept(s.data())); + EXPECT_TRUE(noexcept(s.size())); + EXPECT_TRUE(noexcept(s.length())); + EXPECT_TRUE(noexcept(s.empty())); + EXPECT_TRUE(noexcept(s[0])); + EXPECT_TRUE(noexcept(s.front())); + EXPECT_TRUE(noexcept(s.back())); + EXPECT_TRUE(noexcept(s.begin())); + EXPECT_TRUE(noexcept(s.cbegin())); + EXPECT_TRUE(noexcept(s.end())); + EXPECT_TRUE(noexcept(s.cend())); + EXPECT_TRUE(noexcept(s.rbegin())); + EXPECT_TRUE(noexcept(s.crbegin())); + EXPECT_TRUE(noexcept(s.rend())); + EXPECT_TRUE(noexcept(s.crend())); + EXPECT_TRUE(noexcept(s.remove_prefix(0))); + EXPECT_TRUE(noexcept(s.remove_suffix(0))); +} + +// ConstexprTester exercises expressions in a constexpr context. Simply placing +// the expression in a constexpr function is not enough, as some compilers will +// simply compile the constexpr function as runtime code. Using template +// parameters forces compile-time execution. +template +struct ConstexprTester {}; + +#define ABSL_TEST_CONSTEXPR(expr) \ + do { \ + ABSL_ATTRIBUTE_UNUSED ConstexprTester<(expr, 1)> t; \ + } while (0) + +struct ContainerWithConstexprMethods { + constexpr int size() const { return 1; } + constexpr const int* data() const { return &i; } + const int i; +}; + +TEST(ConstIntSpan, ConstexprTest) { + static constexpr int a[] = {1, 2, 3}; + static constexpr int sized_arr[2] = {1, 2}; + static constexpr ContainerWithConstexprMethods c{1}; + ABSL_TEST_CONSTEXPR(absl::Span()); + ABSL_TEST_CONSTEXPR(absl::Span(a, 2)); + ABSL_TEST_CONSTEXPR(absl::Span(sized_arr)); + ABSL_TEST_CONSTEXPR(absl::Span(c)); + ABSL_TEST_CONSTEXPR(absl::MakeSpan(&a[0], 1)); + ABSL_TEST_CONSTEXPR(absl::MakeSpan(c)); + ABSL_TEST_CONSTEXPR(absl::MakeSpan(a)); + ABSL_TEST_CONSTEXPR(absl::MakeConstSpan(&a[0], 1)); + ABSL_TEST_CONSTEXPR(absl::MakeConstSpan(c)); + ABSL_TEST_CONSTEXPR(absl::MakeConstSpan(a)); + + constexpr absl::Span span = c; + ABSL_TEST_CONSTEXPR(span.data()); + ABSL_TEST_CONSTEXPR(span.size()); + ABSL_TEST_CONSTEXPR(span.length()); + ABSL_TEST_CONSTEXPR(span.empty()); + ABSL_TEST_CONSTEXPR(span.begin()); + ABSL_TEST_CONSTEXPR(span.cbegin()); + ABSL_TEST_CONSTEXPR(span.subspan(0, 0)); + ABSL_TEST_CONSTEXPR(span[0]); +} + +struct BigStruct { + char bytes[10000]; +}; + +TEST(Span, SpanSize) { + EXPECT_LE(sizeof(absl::Span), 2 * sizeof(void*)); + EXPECT_LE(sizeof(absl::Span), 2 * sizeof(void*)); +} + +} // namespace -- cgit v1.2.3