Commit 4132ce25 by Abseil Team Committed by Derek Mauro

Changes imported from Abseil "staging" branch:

  - 0a519d9a4507158267cc515e0c7c83959d94fc78 Fix missing header include when compiling with _GLIBCXX_D... by Alex Strelnikov <strel@google.com>
  - d089af70781d92af9a5de2d84c417ddf2c87689a Internal change by Gennadiy Rozental <rogeeff@google.com>
  - 0d3afc89d3907923ede964d58c6bcca579e8ad65 Test absl::any for exception safety.  This test is tempor... by Jon Cohen <cohenjon@google.com>
  - 29af424b8a3174a7b3e657e478aa30a8a425aee2 Tweak the ABSL type trait library and expand its tests. by Abseil Team <absl-team@google.com>
  - 99ab42b2ebbe466cc3730fb6b16b5fad848f95af Rollback GLIBCXX_DEBUG fix due to internal breakage. by Alex Strelnikov <strel@google.com>
  - 1a5bcb93ee16d4dd2170254e54c4b62b38fbf17b Internal change. by Abseil Team <absl-team@google.com>
  - 46de7d09c7d4aef5b7b5389ce9b4f96b654aac02 absl::string_view::rfind: doc fix. by Abseil Team <absl-team@google.com>
  - edda4c7ddd2d76fbb5b3fd5226b95082083c57d9 Fix string_view_test with c++17/clang/libc++ to address by Xiaoyi Zhang <zhangxy@google.com>

GitOrigin-RevId: 0a519d9a4507158267cc515e0c7c83959d94fc78
Change-Id: Ie27de1be3e79bba011f05e924d34e8fcc62d8de5
parent 0271cd35
...@@ -235,6 +235,7 @@ cc_library( ...@@ -235,6 +235,7 @@ cc_library(
hdrs = ["internal/exception_safety_testing.h"], hdrs = ["internal/exception_safety_testing.h"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
deps = [ deps = [
":base",
":config", ":config",
":pretty_function", ":pretty_function",
"//absl/memory", "//absl/memory",
......
...@@ -33,6 +33,7 @@ list(APPEND BASE_INTERNAL_HEADERS ...@@ -33,6 +33,7 @@ list(APPEND BASE_INTERNAL_HEADERS
"internal/cycleclock.h" "internal/cycleclock.h"
"internal/endian.h" "internal/endian.h"
"internal/exception_testing.h" "internal/exception_testing.h"
"internal/exception_safety_testing.h"
"internal/identity.h" "internal/identity.h"
"internal/invoke.h" "internal/invoke.h"
"internal/log_severity.h" "internal/log_severity.h"
...@@ -43,6 +44,7 @@ list(APPEND BASE_INTERNAL_HEADERS ...@@ -43,6 +44,7 @@ list(APPEND BASE_INTERNAL_HEADERS
"internal/malloc_hook.h" "internal/malloc_hook.h"
"internal/malloc_hook_invoke.h" "internal/malloc_hook_invoke.h"
"internal/per_thread_tls.h" "internal/per_thread_tls.h"
"internal/pretty_function.h"
"internal/raw_logging.h" "internal/raw_logging.h"
"internal/scheduling_mode.h" "internal/scheduling_mode.h"
"internal/spinlock.h" "internal/spinlock.h"
...@@ -57,8 +59,9 @@ list(APPEND BASE_INTERNAL_HEADERS ...@@ -57,8 +59,9 @@ list(APPEND BASE_INTERNAL_HEADERS
# absl_base main library # absl_base main library
list(APPEND BASE_SRC list(APPEND BASE_SRC
"internal/cycleclock.cc" "internal/cycleclock.cc"
"internal/exception_safety_testing.cc"
"internal/raw_logging.cc" "internal/raw_logging.cc"
"internal/spinlock.cc" "internal/spinlock.cc"
"internal/sysinfo.cc" "internal/sysinfo.cc"
...@@ -318,6 +321,20 @@ absl_test( ...@@ -318,6 +321,20 @@ absl_test(
${THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES} ${THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES}
) )
#test exceptions_safety_testing_test
set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES absl::base absl::memory absl::meta absl::strings absl::optional)
absl_test(
TARGET
absl_exception_safety_testing_test
SOURCES
${EXCEPTION_SAFETY_TESTING_TEST_SRC}
PUBLIC_LIBRARIES
${EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES}
PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG}
)
# test absl_malloc_extension_system_malloc_test # test absl_malloc_extension_system_malloc_test
set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_SRC "internal/malloc_extension_test.cc") set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_SRC "internal/malloc_extension_test.cc")
......
...@@ -63,7 +63,7 @@ TEST_F(ThrowingValueTest, Throws) { ...@@ -63,7 +63,7 @@ TEST_F(ThrowingValueTest, Throws) {
// the countdown doesn't hit 0, and doesn't modify the state of the // the countdown doesn't hit 0, and doesn't modify the state of the
// ThrowingValue if it throws // ThrowingValue if it throws
template <typename F> template <typename F>
void TestOp(F&& f) { void TestOp(const F& f) {
UnsetCountdown(); UnsetCountdown();
ExpectNoThrow(f); ExpectNoThrow(f);
...@@ -153,11 +153,21 @@ TEST_F(ThrowingValueTest, ThrowingStreamOps) { ...@@ -153,11 +153,21 @@ TEST_F(ThrowingValueTest, ThrowingStreamOps) {
TestOp([&]() { std::cout << bomb; }); TestOp([&]() { std::cout << bomb; });
} }
template <typename F>
void TestAllocatingOp(const F& f) {
UnsetCountdown();
ExpectNoThrow(f);
SetCountdown();
EXPECT_THROW(f(), exceptions_internal::TestBadAllocException);
UnsetCountdown();
}
TEST_F(ThrowingValueTest, ThrowingAllocatingOps) { TEST_F(ThrowingValueTest, ThrowingAllocatingOps) {
// make_unique calls unqualified operator new, so these exercise the // make_unique calls unqualified operator new, so these exercise the
// ThrowingValue overloads. // ThrowingValue overloads.
TestOp([]() { return absl::make_unique<ThrowingValue<>>(1); }); TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>>(1); });
TestOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); }); TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); });
} }
TEST_F(ThrowingValueTest, NonThrowingMoveCtor) { TEST_F(ThrowingValueTest, NonThrowingMoveCtor) {
...@@ -399,7 +409,8 @@ struct CallOperator { ...@@ -399,7 +409,8 @@ struct CallOperator {
}; };
struct NonNegative { struct NonNegative {
friend testing::AssertionResult AbslCheckInvariants(NonNegative* g) { friend testing::AssertionResult AbslCheckInvariants(
NonNegative* g, absl::InternalAbslNamespaceFinder) {
if (g->i >= 0) return testing::AssertionSuccess(); if (g->i >= 0) return testing::AssertionSuccess();
return testing::AssertionFailure() return testing::AssertionFailure()
<< "i should be non-negative but is " << g->i; << "i should be non-negative but is " << g->i;
...@@ -503,7 +514,8 @@ struct HasReset : public NonNegative { ...@@ -503,7 +514,8 @@ struct HasReset : public NonNegative {
void reset() { i = 0; } void reset() { i = 0; }
friend bool AbslCheckInvariants(HasReset* h) { friend bool AbslCheckInvariants(HasReset* h,
absl::InternalAbslNamespaceFinder) {
h->reset(); h->reset();
return h->i == 0; return h->i == 0;
} }
...@@ -591,7 +603,8 @@ struct ExhaustivenessTester { ...@@ -591,7 +603,8 @@ struct ExhaustivenessTester {
return true; return true;
} }
friend testing::AssertionResult AbslCheckInvariants(ExhaustivenessTester*) { friend testing::AssertionResult AbslCheckInvariants(
ExhaustivenessTester*, absl::InternalAbslNamespaceFinder) {
return testing::AssertionSuccess(); return testing::AssertionSuccess();
} }
......
...@@ -23,8 +23,11 @@ namespace exceptions_internal { ...@@ -23,8 +23,11 @@ namespace exceptions_internal {
int countdown = -1; int countdown = -1;
void MaybeThrow(absl::string_view msg) { void MaybeThrow(absl::string_view msg, bool throw_bad_alloc) {
if (countdown-- == 0) throw TestException(msg); if (countdown-- == 0) {
if (throw_bad_alloc) throw TestBadAllocException(msg);
throw TestException(msg);
}
} }
testing::AssertionResult FailureMessage(const TestException& e, testing::AssertionResult FailureMessage(const TestException& e,
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#include "absl/types/optional.h" #include "absl/types/optional.h"
namespace absl { namespace absl {
struct InternalAbslNamespaceFinder {};
struct AllocInspector; struct AllocInspector;
// A configuration enum for Throwing*. Operations whose flags are set will // A configuration enum for Throwing*. Operations whose flags are set will
...@@ -71,31 +73,45 @@ constexpr bool ThrowingAllowed(NoThrow flags, NoThrow flag) { ...@@ -71,31 +73,45 @@ constexpr bool ThrowingAllowed(NoThrow flags, NoThrow flag) {
class TestException { class TestException {
public: public:
explicit TestException(absl::string_view msg) : msg_(msg) {} explicit TestException(absl::string_view msg) : msg_(msg) {}
absl::string_view what() const { return msg_; } virtual ~TestException() {}
virtual const char* what() const noexcept { return msg_.c_str(); }
private: private:
std::string msg_; std::string msg_;
}; };
// TestBadAllocException exists because allocation functions must throw an
// exception which can be caught by a handler of std::bad_alloc. We use a child
// class of std::bad_alloc so we can customise the error message, and also
// derive from TestException so we don't accidentally end up catching an actual
// bad_alloc exception in TestExceptionSafety.
class TestBadAllocException : public std::bad_alloc, public TestException {
public:
explicit TestBadAllocException(absl::string_view msg)
: TestException(msg) {}
using TestException::what;
};
extern int countdown; extern int countdown;
void MaybeThrow(absl::string_view msg); void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
testing::AssertionResult FailureMessage(const TestException& e, testing::AssertionResult FailureMessage(const TestException& e,
int countdown) noexcept; int countdown) noexcept;
class TrackedObject { class TrackedObject {
public:
TrackedObject(const TrackedObject&) = delete;
TrackedObject(TrackedObject&&) = delete;
protected: protected:
explicit TrackedObject(absl::string_view child_ctor) { explicit TrackedObject(const char* child_ctor) {
if (!GetAllocs().emplace(this, child_ctor).second) { if (!GetAllocs().emplace(this, child_ctor).second) {
ADD_FAILURE() << "Object at address " << static_cast<void*>(this) ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
<< " re-constructed in ctor " << child_ctor; << " re-constructed in ctor " << child_ctor;
} }
} }
TrackedObject(const TrackedObject&) = delete;
TrackedObject(TrackedObject&&) = delete;
static std::unordered_map<TrackedObject*, absl::string_view>& GetAllocs() { static std::unordered_map<TrackedObject*, absl::string_view>& GetAllocs() {
static auto* m = static auto* m =
new std::unordered_map<TrackedObject*, absl::string_view>(); new std::unordered_map<TrackedObject*, absl::string_view>();
...@@ -120,10 +136,10 @@ using FactoryType = typename absl::result_of_t<Factory()>::element_type; ...@@ -120,10 +136,10 @@ using FactoryType = typename absl::result_of_t<Factory()>::element_type;
template <typename Factory, typename Op, typename Checker> template <typename Factory, typename Op, typename Checker>
absl::optional<testing::AssertionResult> TestCheckerAtCountdown( absl::optional<testing::AssertionResult> TestCheckerAtCountdown(
Factory factory, const Op& op, int count, const Checker& check) { Factory factory, const Op& op, int count, const Checker& check) {
exceptions_internal::countdown = count;
auto t_ptr = factory(); auto t_ptr = factory();
absl::optional<testing::AssertionResult> out; absl::optional<testing::AssertionResult> out;
try { try {
exceptions_internal::countdown = count;
op(t_ptr.get()); op(t_ptr.get());
} catch (const exceptions_internal::TestException& e) { } catch (const exceptions_internal::TestException& e) {
out.emplace(check(t_ptr.get())); out.emplace(check(t_ptr.get()));
...@@ -141,6 +157,10 @@ int UpdateOut(Factory factory, const Op& op, int count, const Checker& checker, ...@@ -141,6 +157,10 @@ int UpdateOut(Factory factory, const Op& op, int count, const Checker& checker,
return 0; return 0;
} }
// Declare AbslCheckInvariants so that it can be found eventually via ADL.
// Taking `...` gives it the lowest possible precedence.
void AbslCheckInvariants(...);
// Returns an optional with the result of the check if op fails, or an empty // Returns an optional with the result of the check if op fails, or an empty
// optional if op passes // optional if op passes
template <typename Factory, typename Op, typename... Checkers> template <typename Factory, typename Op, typename... Checkers>
...@@ -148,8 +168,9 @@ absl::optional<testing::AssertionResult> TestAtCountdown( ...@@ -148,8 +168,9 @@ absl::optional<testing::AssertionResult> TestAtCountdown(
Factory factory, const Op& op, int count, const Checkers&... checkers) { Factory factory, const Op& op, int count, const Checkers&... checkers) {
// Don't bother with the checkers if the class invariants are already broken. // Don't bother with the checkers if the class invariants are already broken.
auto out = TestCheckerAtCountdown( auto out = TestCheckerAtCountdown(
factory, op, count, factory, op, count, [](FactoryType<Factory>* t_ptr) {
[](FactoryType<Factory>* t_ptr) { return AbslCheckInvariants(t_ptr); }); return AbslCheckInvariants(t_ptr, InternalAbslNamespaceFinder());
});
if (!out.has_value()) return out; if (!out.has_value()) return out;
// Run each checker, short circuiting after the first failure // Run each checker, short circuiting after the first failure
...@@ -483,7 +504,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject { ...@@ -483,7 +504,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
static void* operator new(size_t s, Args&&... args) noexcept( static void* operator new(size_t s, Args&&... args) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
} }
return ::operator new(s, std::forward<Args>(args)...); return ::operator new(s, std::forward<Args>(args)...);
} }
...@@ -492,7 +513,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject { ...@@ -492,7 +513,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
static void* operator new[](size_t s, Args&&... args) noexcept( static void* operator new[](size_t s, Args&&... args) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
} }
return ::operator new[](s, std::forward<Args>(args)...); return ::operator new[](s, std::forward<Args>(args)...);
} }
...@@ -630,10 +651,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { ...@@ -630,10 +651,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
p->~U(); p->~U();
} }
size_type max_size() const size_type max_size() const noexcept {
noexcept(!exceptions_internal::ThrowingAllowed(Flags,
NoThrow::kNoThrow)) {
ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
return std::numeric_limits<difference_type>::max() / sizeof(value_type); return std::numeric_limits<difference_type>::max() / sizeof(value_type);
} }
...@@ -720,9 +738,12 @@ T TestThrowingCtor(Args&&... args) { ...@@ -720,9 +738,12 @@ T TestThrowingCtor(Args&&... args) {
// Tests that performing operation Op on a T follows exception safety // Tests that performing operation Op on a T follows exception safety
// guarantees. By default only tests the basic guarantee. There must be a // guarantees. By default only tests the basic guarantee. There must be a
// function, AbslCheckInvariants(T*) which returns // function, AbslCheckInvariants(T*, absl::InternalAbslNamespaceFinder) which
// anything convertible to bool and which makes sure the invariants of the type // returns anything convertible to bool and which makes sure the invariants of
// are upheld. This is called before any of the checkers. // the type are upheld. This is called before any of the checkers. The
// InternalAbslNamespaceFinder is unused, and just helps find
// AbslCheckInvariants for absl types which become aliases to std::types in
// C++17.
// //
// Parameters: // Parameters:
// * TFactory: operator() returns a unique_ptr to the type under test (T). It // * TFactory: operator() returns a unique_ptr to the type under test (T). It
...@@ -740,11 +761,13 @@ template <typename TFactory, typename FunctionFromTPtrToVoid, ...@@ -740,11 +761,13 @@ template <typename TFactory, typename FunctionFromTPtrToVoid,
testing::AssertionResult TestExceptionSafety(TFactory factory, testing::AssertionResult TestExceptionSafety(TFactory factory,
FunctionFromTPtrToVoid&& op, FunctionFromTPtrToVoid&& op,
const Checkers&... checkers) { const Checkers&... checkers) {
struct Cleanup {
~Cleanup() { UnsetCountdown(); }
} c;
for (int countdown = 0;; ++countdown) { for (int countdown = 0;; ++countdown) {
auto out = exceptions_internal::TestAtCountdown(factory, op, countdown, auto out = exceptions_internal::TestAtCountdown(factory, op, countdown,
checkers...); checkers...);
if (!out.has_value()) { if (!out.has_value()) {
UnsetCountdown();
return testing::AssertionSuccess(); return testing::AssertionSuccess();
} }
if (!*out) return *out; if (!*out) return *out;
......
...@@ -148,11 +148,16 @@ struct negation : std::integral_constant<bool, !T::value> {}; ...@@ -148,11 +148,16 @@ struct negation : std::integral_constant<bool, !T::value> {};
template <typename T> template <typename T>
struct is_trivially_destructible struct is_trivially_destructible
: std::integral_constant<bool, __has_trivial_destructor(T) && : std::integral_constant<bool, __has_trivial_destructor(T) &&
std::is_destructible<T>::value> { std::is_destructible<T>::value> {
#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
static_assert(std::is_trivially_destructible<T>::value == static constexpr bool compliant = std::is_trivially_destructible<T>::value ==
is_trivially_destructible::value, is_trivially_destructible::value;
"Not compliant with std::is_trivially_destructible"); static_assert(compliant || std::is_trivially_destructible<T>::value,
"Not compliant with std::is_trivially_destructible; "
"Standard: false, Implementation: true");
static_assert(compliant || !std::is_trivially_destructible<T>::value,
"Not compliant with std::is_trivially_destructible; "
"Standard: true, Implementation: false");
#endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
}; };
...@@ -186,18 +191,23 @@ struct is_trivially_destructible ...@@ -186,18 +191,23 @@ struct is_trivially_destructible
// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 // GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452
// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116. // LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116.
// //
// "T obj();" need to be well-formed and not call any non-trivial operation. // "T obj();" need to be well-formed and not call any nontrivial operation.
// Nontrivally destructible types will cause the expression to be nontrivial. // Nontrivally destructible types will cause the expression to be nontrivial.
template <typename T> template <typename T>
struct is_trivially_default_constructible struct is_trivially_default_constructible
: std::integral_constant<bool, : std::integral_constant<bool, __has_trivial_constructor(T) &&
__has_trivial_constructor(T) && std::is_default_constructible<T>::value &&
std::is_default_constructible<T>::value && is_trivially_destructible<T>::value> {
is_trivially_destructible<T>::value> {
#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
static_assert(std::is_trivially_default_constructible<T>::value == static constexpr bool compliant =
is_trivially_default_constructible::value, std::is_trivially_default_constructible<T>::value ==
"Not compliant with std::is_trivially_default_constructible"); is_trivially_default_constructible::value;
static_assert(compliant || std::is_trivially_default_constructible<T>::value,
"Not compliant with std::is_trivially_default_constructible; "
"Standard: false, Implementation: true");
static_assert(compliant || !std::is_trivially_default_constructible<T>::value,
"Not compliant with std::is_trivially_default_constructible; "
"Standard: true, Implementation: false");
#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE #endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
}; };
...@@ -217,12 +227,18 @@ struct is_trivially_default_constructible ...@@ -217,12 +227,18 @@ struct is_trivially_default_constructible
template <typename T> template <typename T>
struct is_trivially_copy_constructible struct is_trivially_copy_constructible
: std::integral_constant<bool, __has_trivial_copy(T) && : std::integral_constant<bool, __has_trivial_copy(T) &&
std::is_copy_constructible<T>::value && std::is_copy_constructible<T>::value &&
is_trivially_destructible<T>::value> { is_trivially_destructible<T>::value> {
#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
static_assert(std::is_trivially_copy_constructible<T>::value == static constexpr bool compliant =
is_trivially_copy_constructible::value, std::is_trivially_copy_constructible<T>::value ==
"Not compliant with std::is_trivially_copy_constructible"); is_trivially_copy_constructible::value;
static_assert(compliant || std::is_trivially_copy_constructible<T>::value,
"Not compliant with std::is_trivially_copy_constructible; "
"Standard: false, Implementation: true");
static_assert(compliant || !std::is_trivially_copy_constructible<T>::value,
"Not compliant with std::is_trivially_copy_constructible; "
"Standard: true, Implementation: false");
#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE #endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
}; };
...@@ -240,15 +256,21 @@ struct is_trivially_copy_constructible ...@@ -240,15 +256,21 @@ struct is_trivially_copy_constructible
// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated // `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
// operand. `is_trivially_assignable<T, U>` requires the assignment to call no // operand. `is_trivially_assignable<T, U>` requires the assignment to call no
// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply // operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
// `is_trivially_assignable<T, const T&>`. // `is_trivially_assignable<T&, const T&>`.
template <typename T> template <typename T>
struct is_trivially_copy_assignable struct is_trivially_copy_assignable
: std::integral_constant<bool, __has_trivial_assign(T) && : std::integral_constant<bool, __has_trivial_assign(T) &&
std::is_copy_assignable<T>::value> { std::is_copy_assignable<T>::value> {
#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
static_assert(std::is_trivially_copy_assignable<T>::value == static constexpr bool compliant =
is_trivially_copy_assignable::value, std::is_trivially_copy_assignable<T>::value ==
"Not compliant with std::is_trivially_copy_assignable"); is_trivially_copy_assignable::value;
static_assert(compliant || std::is_trivially_copy_assignable<T>::value,
"Not compliant with std::is_trivially_copy_assignable; "
"Standard: false, Implementation: true");
static_assert(compliant || !std::is_trivially_copy_assignable<T>::value,
"Not compliant with std::is_trivially_copy_assignable; "
"Standard: true, Implementation: false");
#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE #endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
}; };
......
...@@ -79,31 +79,48 @@ struct IsSTLContainer ...@@ -79,31 +79,48 @@ struct IsSTLContainer
template <typename C, template <typename...> class T, typename = void> template <typename C, template <typename...> class T, typename = void>
struct IsBaseOfSpecializationImpl : std::false_type {}; struct IsBaseOfSpecializationImpl : std::false_type {};
// IsBaseOfSpecializationImpl must have three partial specializations, // IsBaseOfSpecializationImpl needs multiple partial specializations to SFINAE
// because we must only compare templates that take the same number of // on the existence of container dependent types and plug them into the STL
// arguments. Otherwise, for example, std::vector can be compared with std::map, // template.
// and fail to compile because of too few or too many template arguments. template <typename C, template <typename, typename> class T>
// struct IsBaseOfSpecializationImpl<
// We must also SFINAE on the existence of an allocator_type. Otherwise, we may C, T, absl::void_t<typename C::value_type, typename C::allocator_type>>
// try to compare, for example, a std::pair<std::string, std::string> with a : std::is_base_of<C,
// std::vector<std::string, std:std::string>. This would fail to compile, because T<typename C::value_type, typename C::allocator_type>> {};
// of expected properties of the type passed in as the allocator. template <typename C, template <typename, typename, typename> class T>
template <template <typename, typename> class U,
template <typename, typename> class T, typename... Args>
struct IsBaseOfSpecializationImpl< struct IsBaseOfSpecializationImpl<
U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>> C, T,
: std::is_base_of<U<Args...>, T<Args...>> {}; absl::void_t<typename C::key_type, typename C::key_compare,
template <template <typename, typename, typename> class U, typename C::allocator_type>>
template <typename, typename, typename> class T, typename... Args> : std::is_base_of<C, T<typename C::key_type, typename C::key_compare,
typename C::allocator_type>> {};
template <typename C, template <typename, typename, typename, typename> class T>
struct IsBaseOfSpecializationImpl<
C, T,
absl::void_t<typename C::key_type, typename C::mapped_type,
typename C::key_compare, typename C::allocator_type>>
: std::is_base_of<C,
T<typename C::key_type, typename C::mapped_type,
typename C::key_compare, typename C::allocator_type>> {
};
template <typename C, template <typename, typename, typename, typename> class T>
struct IsBaseOfSpecializationImpl< struct IsBaseOfSpecializationImpl<
U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>> C, T,
: std::is_base_of<U<Args...>, T<Args...>> {}; absl::void_t<typename C::key_type, typename C::hasher,
template <template <typename, typename, typename, typename> class U, typename C::key_equal, typename C::allocator_type>>
template <typename, typename, typename, typename> class T, : std::is_base_of<C, T<typename C::key_type, typename C::hasher,
typename... Args> typename C::key_equal, typename C::allocator_type>> {
};
template <typename C,
template <typename, typename, typename, typename, typename> class T>
struct IsBaseOfSpecializationImpl< struct IsBaseOfSpecializationImpl<
U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>> C, T,
: std::is_base_of<U<Args...>, T<Args...>> {}; absl::void_t<typename C::key_type, typename C::mapped_type,
typename C::hasher, typename C::key_equal,
typename C::allocator_type>>
: std::is_base_of<C, T<typename C::key_type, typename C::mapped_type,
typename C::hasher, typename C::key_equal,
typename C::allocator_type>> {};
template <typename C, template <typename...> class T> template <typename C, template <typename...> class T>
using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<absl::decay_t<C>, T>; using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<absl::decay_t<C>, T>;
...@@ -140,31 +157,47 @@ struct IsBaseOfSTLContainer ...@@ -140,31 +157,47 @@ struct IsBaseOfSTLContainer
template <typename C, template <typename...> class T, typename = void> template <typename C, template <typename...> class T, typename = void>
struct IsConvertibleToSpecializationImpl : std::false_type {}; struct IsConvertibleToSpecializationImpl : std::false_type {};
// IsConvertibleToSpecializationImpl must have three partial specializations, // IsConvertibleToSpecializationImpl needs multiple partial specializations to
// because we must only compare templates that take the same number of // SFINAE on the existence of container dependent types and plug them into the
// arguments. Otherwise, for example, std::vector can be compared with std::map, // STL template.
// and fail to compile because of too few or too many template arguments. template <typename C, template <typename, typename> class T>
// struct IsConvertibleToSpecializationImpl<
// We must also SFINAE on the existence of an allocator_type. Otherwise, we may C, T, absl::void_t<typename C::value_type, typename C::allocator_type>>
// try to compare, for example, a std::pair<std::string, std::string> with a : std::is_convertible<
// std::vector<std::string, std:std::string>. This would fail to compile, because C, T<typename C::value_type, typename C::allocator_type>> {};
// of expected properties of the type passed in as the allocator. template <typename C, template <typename, typename, typename> class T>
template <template <typename, typename> class U, struct IsConvertibleToSpecializationImpl<
template <typename, typename> class T, typename... Args> C, T,
absl::void_t<typename C::key_type, typename C::key_compare,
typename C::allocator_type>>
: std::is_convertible<C, T<typename C::key_type, typename C::key_compare,
typename C::allocator_type>> {};
template <typename C, template <typename, typename, typename, typename> class T>
struct IsConvertibleToSpecializationImpl< struct IsConvertibleToSpecializationImpl<
U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>> C, T,
: std::is_convertible<U<Args...>, T<Args...>> {}; absl::void_t<typename C::key_type, typename C::mapped_type,
template <template <typename, typename, typename> class U, typename C::key_compare, typename C::allocator_type>>
template <typename, typename, typename> class T, typename... Args> : std::is_convertible<
C, T<typename C::key_type, typename C::mapped_type,
typename C::key_compare, typename C::allocator_type>> {};
template <typename C, template <typename, typename, typename, typename> class T>
struct IsConvertibleToSpecializationImpl< struct IsConvertibleToSpecializationImpl<
U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>> C, T,
: std::is_convertible<U<Args...>, T<Args...>> {}; absl::void_t<typename C::key_type, typename C::hasher,
template <template <typename, typename, typename, typename> class U, typename C::key_equal, typename C::allocator_type>>
template <typename, typename, typename, typename> class T, : std::is_convertible<
typename... Args> C, T<typename C::key_type, typename C::hasher, typename C::key_equal,
typename C::allocator_type>> {};
template <typename C,
template <typename, typename, typename, typename, typename> class T>
struct IsConvertibleToSpecializationImpl< struct IsConvertibleToSpecializationImpl<
U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>> C, T,
: std::is_convertible<U<Args...>, T<Args...>> {}; absl::void_t<typename C::key_type, typename C::mapped_type,
typename C::hasher, typename C::key_equal,
typename C::allocator_type>>
: std::is_convertible<C, T<typename C::key_type, typename C::mapped_type,
typename C::hasher, typename C::key_equal,
typename C::allocator_type>> {};
template <typename C, template <typename...> class T> template <typename C, template <typename...> class T>
using IsConvertibleToSpecialization = using IsConvertibleToSpecialization =
IsConvertibleToSpecializationImpl<absl::decay_t<C>, T>; IsConvertibleToSpecializationImpl<absl::decay_t<C>, T>;
......
...@@ -419,7 +419,7 @@ class string_view { ...@@ -419,7 +419,7 @@ class string_view {
size_type rfind(string_view s, size_type pos = npos) const size_type rfind(string_view s, size_type pos = npos) const
noexcept; noexcept;
// Overload of `string_view::rfind()` for finding the given character `c` // Overload of `string_view::rfind()` for finding the last given character `c`
// within the `string_view`. // within the `string_view`.
size_type rfind(char c, size_type pos = npos) const noexcept; size_type rfind(char c, size_type pos = npos) const noexcept;
......
...@@ -796,11 +796,25 @@ TEST(StringViewTest, FrontBackSingleChar) { ...@@ -796,11 +796,25 @@ TEST(StringViewTest, FrontBackSingleChar) {
EXPECT_EQ(&c, &csp.back()); EXPECT_EQ(&c, &csp.back());
} }
// `std::string_view::string_view(const char*)` calls
// `std::char_traits<char>::length(const char*)` to get the std::string length. In
// libc++, it doesn't allow `nullptr` in the constexpr context, with the error
// "read of dereferenced null pointer is not allowed in a constant expression".
// At run time, the behavior of `std::char_traits::length()` on `nullptr` is
// undefined by the standard and usually results in crash with libc++. This
// conforms to the standard, but `absl::string_view` implements a different
// behavior for historical reasons. We work around tests that construct
// `string_view` from `nullptr` when using libc++.
#if !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
#define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1
#endif // !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
TEST(StringViewTest, NULLInput) { TEST(StringViewTest, NULLInput) {
absl::string_view s; absl::string_view s;
EXPECT_EQ(s.data(), nullptr); EXPECT_EQ(s.data(), nullptr);
EXPECT_EQ(s.size(), 0); EXPECT_EQ(s.size(), 0);
#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
s = absl::string_view(nullptr); s = absl::string_view(nullptr);
EXPECT_EQ(s.data(), nullptr); EXPECT_EQ(s.data(), nullptr);
EXPECT_EQ(s.size(), 0); EXPECT_EQ(s.size(), 0);
...@@ -808,6 +822,7 @@ TEST(StringViewTest, NULLInput) { ...@@ -808,6 +822,7 @@ TEST(StringViewTest, NULLInput) {
// .ToString() on a absl::string_view with nullptr should produce the empty // .ToString() on a absl::string_view with nullptr should produce the empty
// std::string. // std::string.
EXPECT_EQ("", std::string(s)); EXPECT_EQ("", std::string(s));
#endif // ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
} }
TEST(StringViewTest, Comparisons2) { TEST(StringViewTest, Comparisons2) {
...@@ -879,7 +894,9 @@ TEST(StringViewTest, NullSafeStringView) { ...@@ -879,7 +894,9 @@ TEST(StringViewTest, NullSafeStringView) {
TEST(StringViewTest, ConstexprCompiles) { TEST(StringViewTest, ConstexprCompiles) {
constexpr absl::string_view sp; constexpr absl::string_view sp;
#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
constexpr absl::string_view cstr(nullptr); constexpr absl::string_view cstr(nullptr);
#endif
constexpr absl::string_view cstr_len("cstr", 4); constexpr absl::string_view cstr_len("cstr", 4);
#if defined(ABSL_HAVE_STD_STRING_VIEW) #if defined(ABSL_HAVE_STD_STRING_VIEW)
...@@ -923,10 +940,12 @@ TEST(StringViewTest, ConstexprCompiles) { ...@@ -923,10 +940,12 @@ TEST(StringViewTest, ConstexprCompiles) {
constexpr absl::string_view::iterator const_end_empty = sp.end(); constexpr absl::string_view::iterator const_end_empty = sp.end();
EXPECT_EQ(const_begin_empty, const_end_empty); EXPECT_EQ(const_begin_empty, const_end_empty);
#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
constexpr absl::string_view::iterator const_begin_nullptr = cstr.begin(); constexpr absl::string_view::iterator const_begin_nullptr = cstr.begin();
constexpr absl::string_view::iterator const_end_nullptr = cstr.end(); constexpr absl::string_view::iterator const_end_nullptr = cstr.end();
EXPECT_EQ(const_begin_nullptr, const_end_nullptr); EXPECT_EQ(const_begin_nullptr, const_end_nullptr);
#endif #endif // ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
#endif // !defined(__clang__) || ...
constexpr absl::string_view::iterator const_begin = cstr_len.begin(); constexpr absl::string_view::iterator const_begin = cstr_len.begin();
constexpr absl::string_view::iterator const_end = cstr_len.end(); constexpr absl::string_view::iterator const_end = cstr_len.end();
...@@ -1042,11 +1061,11 @@ TEST(HugeStringView, TwoPointTwoGB) { ...@@ -1042,11 +1061,11 @@ TEST(HugeStringView, TwoPointTwoGB) {
} }
#endif // THREAD_SANITIZER #endif // THREAD_SANITIZER
#ifndef NDEBUG #if !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW)
TEST(NonNegativeLenTest, NonNegativeLen) { TEST(NonNegativeLenTest, NonNegativeLen) {
EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1), "len <= kMaxSize"); EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1), "len <= kMaxSize");
} }
#endif // NDEBUG #endif // !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW)
class StringViewStreamTest : public ::testing::Test { class StringViewStreamTest : public ::testing::Test {
public: public:
......
...@@ -178,4 +178,24 @@ TEST(String, StripLeadingAsciiWhitespace) { ...@@ -178,4 +178,24 @@ TEST(String, StripLeadingAsciiWhitespace) {
EXPECT_EQ(absl::string_view(), absl::StripLeadingAsciiWhitespace(orig)); EXPECT_EQ(absl::string_view(), absl::StripLeadingAsciiWhitespace(orig));
} }
TEST(Strip, StripAsciiWhitespace) {
std::string test2 = "\t \f\r\n\vfoo \t\f\r\v\n";
absl::StripAsciiWhitespace(&test2);
EXPECT_EQ(test2, "foo");
std::string test3 = "bar";
absl::StripAsciiWhitespace(&test3);
EXPECT_EQ(test3, "bar");
std::string test4 = "\t \f\r\n\vfoo";
absl::StripAsciiWhitespace(&test4);
EXPECT_EQ(test4, "foo");
std::string test5 = "foo \t\f\r\v\n";
absl::StripAsciiWhitespace(&test5);
EXPECT_EQ(test5, "foo");
absl::string_view test6("\t \f\r\n\vfoo \t\f\r\v\n");
test6 = absl::StripAsciiWhitespace(test6);
EXPECT_EQ(test6, "foo");
test6 = absl::StripAsciiWhitespace(test6);
EXPECT_EQ(test6, "foo"); // already stripped
}
} // namespace } // namespace
...@@ -28,7 +28,7 @@ licenses(["notice"]) # Apache 2.0 ...@@ -28,7 +28,7 @@ licenses(["notice"]) # Apache 2.0
cc_library( cc_library(
name = "any", name = "any",
hdrs = ["any.h"], hdrs = ["any.h"],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG,
deps = [ deps = [
":bad_any_cast", ":bad_any_cast",
"//absl/base:config", "//absl/base:config",
...@@ -83,6 +83,17 @@ cc_test( ...@@ -83,6 +83,17 @@ cc_test(
], ],
) )
cc_test(
name = "any_exception_safety_test",
srcs = ["any_exception_safety_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
deps = [
":any",
"//absl/base:exception_safety_testing",
"@com_google_googletest//:gtest_main",
],
)
cc_library( cc_library(
name = "span", name = "span",
hdrs = ["span.h"], hdrs = ["span.h"],
......
...@@ -29,6 +29,8 @@ absl_header_library( ...@@ -29,6 +29,8 @@ absl_header_library(
absl_any absl_any
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
absl::utility absl::utility
PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG}
EXPORT_NAME EXPORT_NAME
any any
) )
...@@ -126,6 +128,21 @@ absl_test( ...@@ -126,6 +128,21 @@ absl_test(
${ANY_TEST_PUBLIC_LIBRARIES} ${ANY_TEST_PUBLIC_LIBRARIES}
) )
# test any_exception_safety_test
set(ANY_EXCEPTION_SAFETY_TEST_SRC "any_exception_safety_test.cc")
set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES absl::any absl::base)
absl_test(
TARGET
any_exception_safety_test
SOURCES
${ANY_EXCEPTION_SAFETY_TEST_SRC}
PUBLIC_LIBRARIES
${ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES}
PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG}
)
# test span_test # test span_test
set(SPAN_TEST_SRC "span_test.cc") set(SPAN_TEST_SRC "span_test.cc")
......
// 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 <typeinfo>
#include <vector>
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
using Thrower = absl::ThrowingValue<>;
using ThrowerList = std::initializer_list<Thrower>;
using ThrowerVec = std::vector<Thrower>;
using ThrowingAlloc = absl::ThrowingAllocator<Thrower>;
using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>;
namespace absl {
testing::AssertionResult AbslCheckInvariants(absl::any* a,
InternalAbslNamespaceFinder) {
using testing::AssertionFailure;
using testing::AssertionSuccess;
if (a->has_value()) {
if (a->type() == typeid(void)) {
return AssertionFailure()
<< "A non-empty any should not have type `void`";
}
} else {
if (a->type() != typeid(void)) {
return AssertionFailure()
<< "An empty any should have type void, but has type "
<< a->type().name();
}
}
// Make sure that reset() changes any to a valid state.
a->reset();
if (a->has_value()) {
return AssertionFailure() << "A reset `any` should be valueless";
}
if (a->type() != typeid(void)) {
return AssertionFailure() << "A reset `any` should have type() of `void`, "
"but instead has type "
<< a->type().name();
}
try {
auto unused = absl::any_cast<Thrower>(*a);
static_cast<void>(unused);
return AssertionFailure()
<< "A reset `any` should not be able to be any_cast";
} catch (absl::bad_any_cast) {
} catch (...) {
return AssertionFailure()
<< "Unexpected exception thrown from absl::any_cast";
}
return AssertionSuccess();
}
} // namespace absl
namespace {
class AnyExceptionSafety : public ::testing::Test {
private:
absl::AllocInspector inspector_;
};
testing::AssertionResult AnyIsEmpty(absl::any* a) {
if (!a->has_value()) return testing::AssertionSuccess();
return testing::AssertionFailure()
<< "a should be empty, but instead has value "
<< absl::any_cast<Thrower>(*a).Get();
}
TEST_F(AnyExceptionSafety, Ctors) {
Thrower val(1);
auto with_val = absl::TestThrowingCtor<absl::any>(val);
auto copy = absl::TestThrowingCtor<absl::any>(with_val);
auto in_place =
absl::TestThrowingCtor<absl::any>(absl::in_place_type_t<Thrower>(), 1);
auto in_place_list = absl::TestThrowingCtor<absl::any>(
absl::in_place_type_t<ThrowerVec>(), ThrowerList{val});
auto in_place_list_again =
absl::TestThrowingCtor<absl::any,
absl::in_place_type_t<ThrowingThrowerVec>,
ThrowerList, ThrowingAlloc>(
absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc());
}
struct OneFactory {
std::unique_ptr<absl::any> operator()() const {
return absl::make_unique<absl::any>(absl::in_place_type_t<Thrower>(), 1,
absl::no_throw_ctor);
}
};
struct EmptyFactory {
std::unique_ptr<absl::any> operator()() const {
return absl::make_unique<absl::any>();
}
};
TEST_F(AnyExceptionSafety, Assignment) {
auto thrower_comp = [](const absl::any& l, const absl::any& r) {
return absl::any_cast<Thrower>(l) == absl::any_cast<Thrower>(r);
};
OneFactory one_factory;
absl::ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>
moveable_val(2);
Thrower val(2);
absl::any any_val(val);
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, [&any_val](absl::any* ap) { *ap = any_val; },
absl::StrongGuarantee(one_factory, thrower_comp)));
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, [&val](absl::any* ap) { *ap = val; },
absl::StrongGuarantee(one_factory, thrower_comp)));
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, [&val](absl::any* ap) { *ap = std::move(val); },
absl::StrongGuarantee(one_factory, thrower_comp)));
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory,
[&moveable_val](absl::any* ap) { *ap = std::move(moveable_val); },
absl::StrongGuarantee(one_factory, thrower_comp)));
EmptyFactory empty_factory;
auto empty_comp = [](const absl::any& l, const absl::any& r) {
return !(l.has_value() || r.has_value());
};
EXPECT_TRUE(absl::TestExceptionSafety(
empty_factory, [&any_val](absl::any* ap) { *ap = any_val; },
absl::StrongGuarantee(empty_factory, empty_comp)));
EXPECT_TRUE(absl::TestExceptionSafety(
empty_factory, [&val](absl::any* ap) { *ap = val; },
absl::StrongGuarantee(empty_factory, empty_comp)));
EXPECT_TRUE(absl::TestExceptionSafety(
empty_factory, [&val](absl::any* ap) { *ap = std::move(val); },
absl::StrongGuarantee(empty_factory, empty_comp)));
}
// libstdc++ std::any fails this test
#if !defined(ABSL_HAVE_STD_ANY)
TEST_F(AnyExceptionSafety, Emplace) {
OneFactory one_factory;
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); }, AnyIsEmpty));
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory,
[](absl::any* ap) {
ap->emplace<absl::ThrowingValue<absl::NoThrow::kMoveCtor |
absl::NoThrow::kMoveAssign>>(2);
},
AnyIsEmpty));
EXPECT_TRUE(absl::TestExceptionSafety(one_factory,
[](absl::any* ap) {
std::initializer_list<Thrower> il{
Thrower(2, absl::no_throw_ctor)};
ap->emplace<ThrowerVec>(il);
},
AnyIsEmpty));
EmptyFactory empty_factory;
EXPECT_TRUE(absl::TestExceptionSafety(
empty_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); },
AnyIsEmpty));
EXPECT_TRUE(absl::TestExceptionSafety(empty_factory,
[](absl::any* ap) {
std::initializer_list<Thrower> il{
Thrower(2, absl::no_throw_ctor)};
ap->emplace<ThrowerVec>(il);
},
AnyIsEmpty));
}
#endif // ABSL_HAVE_STD_ANY
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment