diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 07ae20f..e25174a 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -137,11 +137,12 @@ "random/gaussian_distribution.cc" "random/gaussian_distribution.h" "random/internal/distribution_caller.h" - "random/internal/fast_uniform_bits.h" "random/internal/fastmath.h" + "random/internal/fast_uniform_bits.h" "random/internal/gaussian_distribution_gentables.cc" "random/internal/generate_real.h" "random/internal/iostream_state_saver.h" + "random/internal/mock_helpers.h" "random/internal/nonsecure_base.h" "random/internal/pcg_engine.h" "random/internal/platform.h"
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 35cb2e9..745a598 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel
@@ -799,3 +799,16 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "optimization_test", + size = "small", + srcs = ["optimization_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":core_headers", + "//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index b7c01c9..62486f9 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt
@@ -701,3 +701,16 @@ absl::fast_type_id gtest_main ) + +absl_cc_test( + NAME + optimization_test + SRCS + "optimization_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + absl::optional + gtest_main +)
diff --git a/absl/base/call_once.h b/absl/base/call_once.h index bc5ec93..5b468af 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h
@@ -175,7 +175,7 @@ std::memory_order_relaxed) || base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, scheduling_mode) == kOnceInit) { - base_internal::Invoke(std::forward<Callable>(fn), + base_internal::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...); // The call to SpinLockWake below is an optimization, because the waiter // in SpinLockWait is waiting with a short timeout. The atomic load/store
diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index c4eceeb..5c71f32 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// absl::base_internal::Invoke(f, args...) is an implementation of +// absl::base_internal::invoke(f, args...) is an implementation of // INVOKE(f, args...) from section [func.require] of the C++ standard. // // [func.require] @@ -29,7 +29,7 @@ // is not one of the types described in the previous item; // 5. f(t1, t2, ..., tN) in all other cases. // -// The implementation is SFINAE-friendly: substitution failure within Invoke() +// The implementation is SFINAE-friendly: substitution failure within invoke() // isn't an error. #ifndef ABSL_BASE_INTERNAL_INVOKE_H_ @@ -170,13 +170,13 @@ // The result type of Invoke<F, Args...>. template <typename F, typename... Args> -using InvokeT = decltype(Invoker<F, Args...>::type::Invoke( +using invoke_result_t = decltype(Invoker<F, Args...>::type::Invoke( std::declval<F>(), std::declval<Args>()...)); // Invoke(f, args...) is an implementation of INVOKE(f, args...) from section // [func.require] of the C++ standard. template <typename F, typename... Args> -InvokeT<F, Args...> Invoke(F&& f, Args&&... args) { +invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) { return Invoker<F, Args...>::type::Invoke(std::forward<F>(f), std::forward<Args>(args)...); }
diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 6aa613c..bcdef36 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc
@@ -86,71 +86,73 @@ int member; }; -// CallMaybeWithArg(f) resolves either to Invoke(f) or Invoke(f, 42), depending +// CallMaybeWithArg(f) resolves either to invoke(f) or invoke(f, 42), depending // on which one is valid. template <typename F> -decltype(Invoke(std::declval<const F&>())) CallMaybeWithArg(const F& f) { - return Invoke(f); +decltype(base_internal::invoke(std::declval<const F&>())) CallMaybeWithArg( + const F& f) { + return base_internal::invoke(f); } template <typename F> -decltype(Invoke(std::declval<const F&>(), 42)) CallMaybeWithArg(const F& f) { - return Invoke(f, 42); +decltype(base_internal::invoke(std::declval<const F&>(), 42)) CallMaybeWithArg( + const F& f) { + return base_internal::invoke(f, 42); } TEST(InvokeTest, Function) { - EXPECT_EQ(1, Invoke(Function, 3, 2)); - EXPECT_EQ(1, Invoke(&Function, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(Function, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Function, 3, 2)); } TEST(InvokeTest, NonCopyableArgument) { - EXPECT_EQ(42, Invoke(Sink, make_unique<int>(42))); + EXPECT_EQ(42, base_internal::invoke(Sink, make_unique<int>(42))); } TEST(InvokeTest, NonCopyableResult) { - EXPECT_THAT(Invoke(Factory, 42), ::testing::Pointee(42)); + EXPECT_THAT(base_internal::invoke(Factory, 42), ::testing::Pointee(42)); } -TEST(InvokeTest, VoidResult) { - Invoke(NoOp); -} +TEST(InvokeTest, VoidResult) { base_internal::invoke(NoOp); } TEST(InvokeTest, ConstFunctor) { - EXPECT_EQ(1, Invoke(ConstFunctor(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(ConstFunctor(), 3, 2)); } TEST(InvokeTest, MutableFunctor) { MutableFunctor f; - EXPECT_EQ(1, Invoke(f, 3, 2)); - EXPECT_EQ(1, Invoke(MutableFunctor(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(f, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(MutableFunctor(), 3, 2)); } TEST(InvokeTest, EphemeralFunctor) { EphemeralFunctor f; - EXPECT_EQ(1, Invoke(std::move(f), 3, 2)); - EXPECT_EQ(1, Invoke(EphemeralFunctor(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::move(f), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(EphemeralFunctor(), 3, 2)); } TEST(InvokeTest, OverloadedFunctor) { OverloadedFunctor f; const OverloadedFunctor& cf = f; - EXPECT_EQ("&", Invoke(f)); - EXPECT_EQ("& 42", Invoke(f, " 42")); + EXPECT_EQ("&", base_internal::invoke(f)); + EXPECT_EQ("& 42", base_internal::invoke(f, " 42")); - EXPECT_EQ("const&", Invoke(cf)); - EXPECT_EQ("const& 42", Invoke(cf, " 42")); + EXPECT_EQ("const&", base_internal::invoke(cf)); + EXPECT_EQ("const& 42", base_internal::invoke(cf, " 42")); - EXPECT_EQ("&&", Invoke(std::move(f))); - EXPECT_EQ("&& 42", Invoke(std::move(f), " 42")); + EXPECT_EQ("&&", base_internal::invoke(std::move(f))); + + OverloadedFunctor f2; + EXPECT_EQ("&& 42", base_internal::invoke(std::move(f2), " 42")); } TEST(InvokeTest, ReferenceWrapper) { ConstFunctor cf; MutableFunctor mf; - EXPECT_EQ(1, Invoke(std::cref(cf), 3, 2)); - EXPECT_EQ(1, Invoke(std::ref(cf), 3, 2)); - EXPECT_EQ(1, Invoke(std::ref(mf), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::cref(cf), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::ref(cf), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::ref(mf), 3, 2)); } TEST(InvokeTest, MemberFunction) { @@ -158,58 +160,62 @@ std::unique_ptr<const Class> cp(new Class); std::unique_ptr<volatile Class> vp(new Class); - EXPECT_EQ(1, Invoke(&Class::Method, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::Method, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::Method, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefMethod, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefRefMethod, std::move(*p), 3, 2)); // NOLINT - EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::Method, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::Method, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::Method, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefRefMethod, std::move(*p), 3, + 2)); // NOLINT + EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, *cp, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, cp, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, cp.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, *cp, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *vp, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, vp, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, vp.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, *vp, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::Method, make_unique<Class>(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique<Class>(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique<const Class>(), 3, 2)); + EXPECT_EQ(1, + base_internal::invoke(&Class::Method, make_unique<Class>(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, make_unique<Class>(), + 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, + make_unique<const Class>(), 3, 2)); } TEST(InvokeTest, DataMember) { std::unique_ptr<Class> p(new Class{42}); std::unique_ptr<const Class> cp(new Class{42}); - EXPECT_EQ(42, Invoke(&Class::member, p)); - EXPECT_EQ(42, Invoke(&Class::member, *p)); - EXPECT_EQ(42, Invoke(&Class::member, p.get())); + EXPECT_EQ(42, base_internal::invoke(&Class::member, p)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, *p)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, p.get())); - Invoke(&Class::member, p) = 42; - Invoke(&Class::member, p.get()) = 42; + base_internal::invoke(&Class::member, p) = 42; + base_internal::invoke(&Class::member, p.get()) = 42; - EXPECT_EQ(42, Invoke(&Class::member, cp)); - EXPECT_EQ(42, Invoke(&Class::member, *cp)); - EXPECT_EQ(42, Invoke(&Class::member, cp.get())); + EXPECT_EQ(42, base_internal::invoke(&Class::member, cp)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, *cp)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, cp.get())); } TEST(InvokeTest, FlipFlop) { FlipFlop obj = {42}; // This call could resolve to (obj.*&FlipFlop::ConstMethod)() or // ((*obj).*&FlipFlop::ConstMethod)(). We verify that it's the former. - EXPECT_EQ(42, Invoke(&FlipFlop::ConstMethod, obj)); - EXPECT_EQ(42, Invoke(&FlipFlop::member, obj)); + EXPECT_EQ(42, base_internal::invoke(&FlipFlop::ConstMethod, obj)); + EXPECT_EQ(42, base_internal::invoke(&FlipFlop::member, obj)); } TEST(InvokeTest, SfinaeFriendly) {
diff --git a/absl/base/optimization_test.cc b/absl/base/optimization_test.cc new file mode 100644 index 0000000..894b68f --- /dev/null +++ b/absl/base/optimization_test.cc
@@ -0,0 +1,132 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/optimization.h" + +#include "gtest/gtest.h" +#include "absl/types/optional.h" + +namespace { + +// Tests for the ABSL_PREDICT_TRUE and ABSL_PREDICT_FALSE macros. +// The tests only verify that the macros are functionally correct - i.e. code +// behaves as if they weren't used. They don't try to check their impact on +// optimization. + +TEST(PredictTest, PredictTrue) { + EXPECT_TRUE(ABSL_PREDICT_TRUE(true)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(false)); + EXPECT_TRUE(ABSL_PREDICT_TRUE(1 == 1)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(1 == 2)); + + if (ABSL_PREDICT_TRUE(false)) ADD_FAILURE(); + if (!ABSL_PREDICT_TRUE(true)) ADD_FAILURE(); + + EXPECT_TRUE(ABSL_PREDICT_TRUE(true) && true); + EXPECT_TRUE(ABSL_PREDICT_TRUE(true) || false); +} + +TEST(PredictTest, PredictFalse) { + EXPECT_TRUE(ABSL_PREDICT_FALSE(true)); + EXPECT_FALSE(ABSL_PREDICT_FALSE(false)); + EXPECT_TRUE(ABSL_PREDICT_FALSE(1 == 1)); + EXPECT_FALSE(ABSL_PREDICT_FALSE(1 == 2)); + + if (ABSL_PREDICT_FALSE(false)) ADD_FAILURE(); + if (!ABSL_PREDICT_FALSE(true)) ADD_FAILURE(); + + EXPECT_TRUE(ABSL_PREDICT_FALSE(true) && true); + EXPECT_TRUE(ABSL_PREDICT_FALSE(true) || false); +} + +TEST(PredictTest, OneEvaluation) { + // Verify that the expression is only evaluated once. + int x = 0; + if (ABSL_PREDICT_TRUE((++x) == 0)) ADD_FAILURE(); + EXPECT_EQ(x, 1); + if (ABSL_PREDICT_FALSE((++x) == 0)) ADD_FAILURE(); + EXPECT_EQ(x, 2); +} + +TEST(PredictTest, OperatorOrder) { + // Verify that operator order inside and outside the macro behaves well. + // These would fail for a naive '#define ABSL_PREDICT_TRUE(x) x' + EXPECT_TRUE(ABSL_PREDICT_TRUE(1 && 2) == true); + EXPECT_TRUE(ABSL_PREDICT_FALSE(1 && 2) == true); + EXPECT_TRUE(!ABSL_PREDICT_TRUE(1 == 2)); + EXPECT_TRUE(!ABSL_PREDICT_FALSE(1 == 2)); +} + +TEST(PredictTest, Pointer) { + const int x = 3; + const int *good_intptr = &x; + const int *null_intptr = nullptr; + EXPECT_TRUE(ABSL_PREDICT_TRUE(good_intptr)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(null_intptr)); + // The following doesn't compile: + // EXPECT_TRUE(ABSL_PREDICT_FALSE(good_intptr)); + // EXPECT_FALSE(ABSL_PREDICT_FALSE(null_intptr)); +} + +TEST(PredictTest, Optional) { + // Note: An optional's truth value is the value's existence, not its truth. + absl::optional<bool> has_value(false); + absl::optional<bool> no_value; + EXPECT_TRUE(ABSL_PREDICT_TRUE(has_value)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(no_value)); + // The following doesn't compile: + // EXPECT_TRUE(ABSL_PREDICT_FALSE(has_value)); + // EXPECT_FALSE(ABSL_PREDICT_FALSE(no_value)); +} + +class ImplictlyConvertibleToBool { + public: + explicit ImplictlyConvertibleToBool(bool value) : value_(value) {} + operator bool() const { // NOLINT(google-explicit-constructor) + return value_; + } + + private: + bool value_; +}; + +TEST(PredictTest, ImplicitBoolConversion) { + const ImplictlyConvertibleToBool is_true(true); + const ImplictlyConvertibleToBool is_false(false); + if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE(); + if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE(); +} + +class ExplictlyConvertibleToBool { + public: + explicit ExplictlyConvertibleToBool(bool value) : value_(value) {} + explicit operator bool() const { return value_; } + + private: + bool value_; +}; + +TEST(PredictTest, ExplicitBoolConversion) { + const ExplictlyConvertibleToBool is_true(true); + const ExplictlyConvertibleToBool is_false(false); + if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE(); + // The following doesn't compile: + // if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE(); + // if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE(); +} + +} // namespace
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index bbdb5f4..1738d29 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc
@@ -52,6 +52,7 @@ using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::IsEmpty; +using ::testing::IsNull; using ::testing::Pair; template <typename T, typename U> @@ -2404,6 +2405,109 @@ m[n]; } +TEST(Btree, SetRangeConstructorAndInsertSupportExplicitConversionComparable) { + const absl::string_view names[] = {"n1", "n2"}; + + absl::btree_set<std::string> name_set1{std::begin(names), std::end(names)}; + EXPECT_THAT(name_set1, ElementsAreArray(names)); + + absl::btree_set<std::string> name_set2; + name_set2.insert(std::begin(names), std::end(names)); + EXPECT_THAT(name_set2, ElementsAreArray(names)); +} + +// A type that is explicitly convertible from int and counts constructor calls. +struct ConstructorCounted { + explicit ConstructorCounted(int i) : i(i) { ++constructor_calls; } + bool operator==(int other) const { return i == other; } + + int i; + static int constructor_calls; +}; +int ConstructorCounted::constructor_calls = 0; + +struct ConstructorCountedCompare { + bool operator()(int a, const ConstructorCounted &b) const { return a < b.i; } + bool operator()(const ConstructorCounted &a, int b) const { return a.i < b; } + bool operator()(const ConstructorCounted &a, + const ConstructorCounted &b) const { + return a.i < b.i; + } + using is_transparent = void; +}; + +TEST(Btree, + SetRangeConstructorAndInsertExplicitConvComparableLimitConstruction) { + const int i[] = {0, 1, 1}; + ConstructorCounted::constructor_calls = 0; + + absl::btree_set<ConstructorCounted, ConstructorCountedCompare> set{ + std::begin(i), std::end(i)}; + EXPECT_THAT(set, ElementsAre(0, 1)); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); + + set.insert(std::begin(i), std::end(i)); + EXPECT_THAT(set, ElementsAre(0, 1)); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); +} + +TEST(Btree, + SetRangeConstructorAndInsertSupportExplicitConversionNonComparable) { + const int i[] = {0, 1}; + + absl::btree_set<std::vector<void *>> s1{std::begin(i), std::end(i)}; + EXPECT_THAT(s1, ElementsAre(IsEmpty(), ElementsAre(IsNull()))); + + absl::btree_set<std::vector<void *>> s2; + s2.insert(std::begin(i), std::end(i)); + EXPECT_THAT(s2, ElementsAre(IsEmpty(), ElementsAre(IsNull()))); +} + +// GCC 4.9 has a bug in the std::pair constructors that prevents explicit +// conversions between pair types. +#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5 +TEST(Btree, MapRangeConstructorAndInsertSupportExplicitConversionComparable) { + const std::pair<absl::string_view, int> names[] = {{"n1", 1}, {"n2", 2}}; + + absl::btree_map<std::string, int> name_map1{std::begin(names), + std::end(names)}; + EXPECT_THAT(name_map1, ElementsAre(Pair("n1", 1), Pair("n2", 2))); + + absl::btree_map<std::string, int> name_map2; + name_map2.insert(std::begin(names), std::end(names)); + EXPECT_THAT(name_map2, ElementsAre(Pair("n1", 1), Pair("n2", 2))); +} + +TEST(Btree, + MapRangeConstructorAndInsertExplicitConvComparableLimitConstruction) { + const std::pair<int, int> i[] = {{0, 1}, {1, 2}, {1, 3}}; + ConstructorCounted::constructor_calls = 0; + + absl::btree_map<ConstructorCounted, int, ConstructorCountedCompare> map{ + std::begin(i), std::end(i)}; + EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2))); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); + + map.insert(std::begin(i), std::end(i)); + EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2))); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); +} + +TEST(Btree, + MapRangeConstructorAndInsertSupportExplicitConversionNonComparable) { + const std::pair<int, int> i[] = {{0, 1}, {1, 2}}; + + absl::btree_map<std::vector<void *>, int> m1{std::begin(i), std::end(i)}; + EXPECT_THAT(m1, + ElementsAre(Pair(IsEmpty(), 1), Pair(ElementsAre(IsNull()), 2))); + + absl::btree_map<std::vector<void *>, int> m2; + m2.insert(std::begin(i), std::end(i)); + EXPECT_THAT(m2, + ElementsAre(Pair(IsEmpty(), 1), Pair(ElementsAre(IsNull()), 2))); +} +#endif + } // namespace } // namespace container_internal ABSL_NAMESPACE_END
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index f52fe23..996afa6 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h
@@ -290,9 +290,12 @@ }; using is_map_container = std::true_type; - static const Key &key(const value_type &value) { return value.first; } - static const Key &key(const init_type &init) { return init.first; } + template <typename V> + static auto key(const V &value) -> decltype(value.first) { + return value.first; + } static const Key &key(const slot_type *s) { return slot_policy::key(s); } + static const Key &key(slot_type *s) { return slot_policy::key(s); } static mapped_type &value(value_type *value) { return value->second; } }; @@ -353,8 +356,10 @@ using value_compare = typename set_params::common_params::key_compare; using is_map_container = std::false_type; - static const Key &key(const value_type &value) { return value; } + template <typename V> + static const V &key(const V &value) { return value; } static const Key &key(const slot_type *slot) { return *slot; } + static const Key &key(slot_type *slot) { return *slot; } }; // An adapter class that converts a lower-bound compare into an upper-bound @@ -1020,6 +1025,7 @@ class btree { using node_type = btree_node<Params>; using is_key_compare_to = typename Params::is_key_compare_to; + using init_type = typename Params::init_type; // We use a static empty node for the root/leftmost/rightmost of empty btrees // in order to avoid branching in begin()/end(). @@ -1184,8 +1190,8 @@ // boolean return value indicates whether insertion succeeded or failed. // Requirement: if `key` already exists in the btree, does not consume `args`. // Requirement: `key` is never referenced after consuming `args`. - template <typename... Args> - std::pair<iterator, bool> insert_unique(const key_type &key, Args &&... args); + template <typename K, typename... Args> + std::pair<iterator, bool> insert_unique(const K &key, Args &&... args); // Inserts with hint. Checks to see if the value should be placed immediately // before `position` in the tree. If so, then the insertion will take @@ -1193,14 +1199,23 @@ // logarithmic time as if a call to insert_unique() were made. // Requirement: if `key` already exists in the btree, does not consume `args`. // Requirement: `key` is never referenced after consuming `args`. - template <typename... Args> + template <typename K, typename... Args> std::pair<iterator, bool> insert_hint_unique(iterator position, - const key_type &key, + const K &key, Args &&... args); // Insert a range of values into the btree. + // Note: the first overload avoids constructing a value_type if the key + // already exists in the btree. + template <typename InputIterator, + typename = decltype(std::declval<const key_compare &>()( + params_type::key(*std::declval<InputIterator>()), + std::declval<const key_type &>()))> + void insert_iterator_unique(InputIterator b, InputIterator e, int); + // We need the second overload for cases in which we need to construct a + // value_type in order to compare it with the keys already in the btree. template <typename InputIterator> - void insert_iterator_unique(InputIterator b, InputIterator e); + void insert_iterator_unique(InputIterator b, InputIterator e, char); // Inserts a value into the btree. template <typename ValueType> @@ -1855,8 +1870,8 @@ } template <typename P> -template <typename... Args> -auto btree<P>::insert_unique(const key_type &key, Args &&... args) +template <typename K, typename... Args> +auto btree<P>::insert_unique(const K &key, Args &&... args) -> std::pair<iterator, bool> { if (empty()) { mutable_root() = rightmost_ = new_leaf_root_node(1); @@ -1881,8 +1896,8 @@ } template <typename P> -template <typename... Args> -inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key, +template <typename K, typename... Args> +inline auto btree<P>::insert_hint_unique(iterator position, const K &key, Args &&... args) -> std::pair<iterator, bool> { if (!empty()) { @@ -1906,14 +1921,23 @@ } template <typename P> -template <typename InputIterator> -void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e) { +template <typename InputIterator, typename> +void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e, int) { for (; b != e; ++b) { insert_hint_unique(end(), params_type::key(*b), *b); } } template <typename P> +template <typename InputIterator> +void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e, char) { + for (; b != e; ++b) { + init_type value(*b); + insert_hint_unique(end(), params_type::key(value), std::move(value)); + } +} + +template <typename P> template <typename ValueType> auto btree<P>::insert_multi(const key_type &key, ValueType &&v) -> iterator { if (empty()) {
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index 734c90e..d0fc645 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h
@@ -289,10 +289,10 @@ } template <typename InputIterator> void insert(InputIterator b, InputIterator e) { - this->tree_.insert_iterator_unique(b, e); + this->tree_.insert_iterator_unique(b, e, 0); } void insert(std::initializer_list<init_type> init) { - this->tree_.insert_iterator_unique(init.begin(), init.end()); + this->tree_.insert_iterator_unique(init.begin(), init.end(), 0); } insert_return_type insert(node_type &&node) { if (!node) return {this->end(), false, node_type()};
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index df0f2b2..1f304b6 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -497,6 +497,18 @@ return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7); } +inline void AssertIsFull(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, or the table might have rehashed."); +} + +inline void AssertIsValid(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT((ctrl == nullptr || IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, or the table might have rehashed."); +} + // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). @@ -617,7 +629,7 @@ // PRECONDITION: not an end() iterator. reference operator*() const { - assert_is_full(); + AssertIsFull(ctrl_); return PolicyTraits::element(slot_); } @@ -626,7 +638,7 @@ // PRECONDITION: not an end() iterator. iterator& operator++() { - assert_is_full(); + AssertIsFull(ctrl_); ++ctrl_; ++slot_; skip_empty_or_deleted(); @@ -640,8 +652,8 @@ } friend bool operator==(const iterator& a, const iterator& b) { - a.assert_is_valid(); - b.assert_is_valid(); + AssertIsValid(a.ctrl_); + AssertIsValid(b.ctrl_); return a.ctrl_ == b.ctrl_; } friend bool operator!=(const iterator& a, const iterator& b) { @@ -655,13 +667,6 @@ ABSL_INTERNAL_ASSUME(ctrl != nullptr); } - void assert_is_full() const { - ABSL_HARDENING_ASSERT(ctrl_ != nullptr && IsFull(*ctrl_)); - } - void assert_is_valid() const { - ABSL_HARDENING_ASSERT(ctrl_ == nullptr || IsFull(*ctrl_)); - } - void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); @@ -1174,7 +1179,7 @@ // This overload is necessary because otherwise erase<K>(const K&) would be // a better match if non-const iterator is passed as an argument. void erase(iterator it) { - it.assert_is_full(); + AssertIsFull(it.ctrl_); PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1208,7 +1213,7 @@ } node_type extract(const_iterator position) { - position.inner_.assert_is_full(); + AssertIsFull(position.inner_.ctrl_); auto node = CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_); erase_meta_only(position);
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 2fc8559..6befa96 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -1791,7 +1791,7 @@ IntTable t; // Extra simple "regexp" as regexp support is highly varied across platforms. - constexpr char kDeathMsg[] = "IsFull"; + constexpr char kDeathMsg[] = "Invalid operation on iterator"; EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); }
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index ff62792..d336246 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel
@@ -100,6 +100,7 @@ "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/memory", + "//absl/strings", "@com_google_googletest//:gtest", ], )
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 995e888..c597df8 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt
@@ -85,6 +85,7 @@ absl::core_headers absl::memory absl::raw_logging_internal + absl::strings gmock )
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index 8789e69..663d774 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h
@@ -22,6 +22,7 @@ #include <cstdint> #include "absl/base/config.h" +#include "absl/strings/string_view.h" #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set @@ -45,7 +46,7 @@ // // This is not async-signal-safe. bool ForEachSection(int fd, - const std::function<bool(const std::string& name, + const std::function<bool(absl::string_view name, const ElfW(Shdr) &)>& callback); // Gets the section header for the given name, if it exists. Returns true on
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index ec86f9a..c05424e 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc
@@ -74,6 +74,7 @@ #include "absl/base/port.h" #include "absl/debugging/internal/demangle.h" #include "absl/debugging/internal/vdso_support.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -498,7 +499,7 @@ const int kMaxSectionNameLen = 64; bool ForEachSection(int fd, - const std::function<bool(const std::string &name, + const std::function<bool(absl::string_view name, const ElfW(Shdr) &)> &callback) { ElfW(Ehdr) elf_header; if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { @@ -520,7 +521,7 @@ return false; } off_t name_offset = shstrtab.sh_offset + out.sh_name; - char header_name[kMaxSectionNameLen + 1]; + char header_name[kMaxSectionNameLen]; ssize_t n_read = ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset); if (n_read == -1) { @@ -529,9 +530,8 @@ // Long read? return false; } - header_name[n_read] = '\0'; - std::string name(header_name); + absl::string_view name(header_name, strnlen(header_name, n_read)); if (!callback(name, out)) { break; }
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index e476d82..43f6554 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc
@@ -32,6 +32,7 @@ #include "absl/base/optimization.h" #include "absl/debugging/internal/stack_consumption.h" #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" using testing::Contains; @@ -401,8 +402,8 @@ std::vector<std::string> sections; ASSERT_TRUE(absl::debugging_internal::ForEachSection( - fd, [§ions](const std::string &name, const ElfW(Shdr) &) { - sections.push_back(name); + fd, [§ions](const absl::string_view name, const ElfW(Shdr) &) { + sections.emplace_back(name); return true; }));
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 37c9a10..006911f 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel
@@ -169,9 +169,6 @@ ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/flags:__pkg__", - ], deps = [ ":commandlineflag", ":commandlineflag_internal",
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h index c72eebe..6f5006a 100644 --- a/absl/flags/internal/registry.h +++ b/absl/flags/internal/registry.h
@@ -28,15 +28,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN - -// TODO(rogeeff): remove this declaration -CommandLineFlag* FindCommandLineFlag(absl::string_view name); - namespace flags_internal { -// TODO(rogeeff): remove this alias -using absl::FindCommandLineFlag; - // Executes specified visitor for each non-retired flag in the registry. // Requires the caller hold the registry lock. void ForEachFlagUnlocked(std::function<void(CommandLineFlag&)> visitor);
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h index 370acc5..6e03ac2 100644 --- a/absl/functional/function_ref.h +++ b/absl/functional/function_ref.h
@@ -90,7 +90,7 @@ // Used to disable constructors for objects that are not compatible with the // signature of this FunctionRef. template <typename F, - typename FR = absl::base_internal::InvokeT<F, Args&&...>> + typename FR = absl::base_internal::invoke_result_t<F, Args&&...>> using EnableIfCompatible = typename std::enable_if<std::is_void<R>::value || std::is_convertible<FR, R>::value>::type;
diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h index a4d95da..45f52de 100644 --- a/absl/functional/internal/front_binder.h +++ b/absl/functional/internal/front_binder.h
@@ -33,7 +33,7 @@ // Invoke the method, expanding the tuple of bound arguments. template <class R, class Tuple, size_t... Idx, class... Args> R Apply(Tuple&& bound, absl::index_sequence<Idx...>, Args&&... free) { - return base_internal::Invoke( + return base_internal::invoke( absl::forward<Tuple>(bound).template get<Idx>()..., absl::forward<Args>(free)...); } @@ -50,22 +50,22 @@ constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts) : bound_args_(absl::forward<Ts>(ts)...) {} - template <class... FreeArgs, - class R = base_internal::InvokeT<F&, BoundArgs&..., FreeArgs&&...>> + template <class... FreeArgs, class R = base_internal::invoke_result_t< + F&, BoundArgs&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) & { return functional_internal::Apply<R>(bound_args_, Idx(), absl::forward<FreeArgs>(free_args)...); } template <class... FreeArgs, - class R = base_internal::InvokeT<const F&, const BoundArgs&..., - FreeArgs&&...>> + class R = base_internal::invoke_result_t< + const F&, const BoundArgs&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) const& { return functional_internal::Apply<R>(bound_args_, Idx(), absl::forward<FreeArgs>(free_args)...); } - template <class... FreeArgs, class R = base_internal::InvokeT< + template <class... FreeArgs, class R = base_internal::invoke_result_t< F&&, BoundArgs&&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) && { // This overload is called when *this is an rvalue. If some of the bound @@ -75,8 +75,8 @@ } template <class... FreeArgs, - class R = base_internal::InvokeT<const F&&, const BoundArgs&&..., - FreeArgs&&...>> + class R = base_internal::invoke_result_t< + const F&&, const BoundArgs&&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) const&& { // This overload is called when *this is an rvalue. If some of the bound // arguments are stored by value or rvalue reference, we move them.
diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h index d157505..b5bb8b4 100644 --- a/absl/functional/internal/function_ref.h +++ b/absl/functional/internal/function_ref.h
@@ -71,14 +71,14 @@ R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) { auto o = static_cast<const Obj*>(ptr.obj); return static_cast<R>( - absl::base_internal::Invoke(*o, std::forward<Args>(args)...)); + absl::base_internal::invoke(*o, std::forward<Args>(args)...)); } template <typename Fun, typename R, typename... Args> R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) { auto f = reinterpret_cast<Fun>(ptr.fun); return static_cast<R>( - absl::base_internal::Invoke(f, std::forward<Args>(args)...)); + absl::base_internal::invoke(f, std::forward<Args>(args)...)); } template <typename Sig>
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index ba87d2f..75689bb 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h
@@ -219,7 +219,7 @@ // This metafunction is designed to be a drop-in replacement for the C++17 // `std::conjunction` metafunction. template <typename... Ts> -struct conjunction; +struct conjunction : std::true_type {}; template <typename T, typename... Ts> struct conjunction<T, Ts...> @@ -228,9 +228,6 @@ template <typename T> struct conjunction<T> : T {}; -template <> -struct conjunction<> : std::true_type {}; - // disjunction // // Performs a compile-time logical OR operation on the passed types (which @@ -241,7 +238,7 @@ // This metafunction is designed to be a drop-in replacement for the C++17 // `std::disjunction` metafunction. template <typename... Ts> -struct disjunction; +struct disjunction : std::false_type {}; template <typename T, typename... Ts> struct disjunction<T, Ts...> : @@ -250,9 +247,6 @@ template <typename T> struct disjunction<T> : T {}; -template <> -struct disjunction<> : std::false_type {}; - // negation // // Performs a compile-time logical NOT operation on the passed type (which
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index e09e52d..da3af4d 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel
@@ -35,6 +35,7 @@ copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + "//absl/base:bits", "//absl/base:config", "//absl/base:core_headers", ],
diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt index 242889f..1e12d80 100644 --- a/absl/numeric/CMakeLists.txt +++ b/absl/numeric/CMakeLists.txt
@@ -26,6 +26,7 @@ COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::bits absl::config absl::core_headers PUBLIC
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index b605a87..e21e5e9 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc
@@ -15,6 +15,7 @@ #include "absl/numeric/int128.h" #include <stddef.h> + #include <cassert> #include <iomanip> #include <ostream> // NOLINT(readability/streams) @@ -22,6 +23,9 @@ #include <string> #include <type_traits> +#include "absl/base/internal/bits.h" +#include "absl/base/optimization.h" + namespace absl { ABSL_NAMESPACE_BEGIN @@ -31,44 +35,26 @@ namespace { // Returns the 0-based position of the last set bit (i.e., most significant bit) -// in the given uint64_t. The argument may not be 0. +// in the given uint128. The argument is not 0. // // For example: // Given: 5 (decimal) == 101 (binary) // Returns: 2 -#define STEP(T, n, pos, sh) \ - do { \ - if ((n) >= (static_cast<T>(1) << (sh))) { \ - (n) = (n) >> (sh); \ - (pos) |= (sh); \ - } \ - } while (0) -static inline int Fls64(uint64_t n) { - assert(n != 0); - int pos = 0; - STEP(uint64_t, n, pos, 0x20); - uint32_t n32 = static_cast<uint32_t>(n); - STEP(uint32_t, n32, pos, 0x10); - STEP(uint32_t, n32, pos, 0x08); - STEP(uint32_t, n32, pos, 0x04); - return pos + ((uint64_t{0x3333333322221100} >> (n32 << 2)) & 0x3); -} -#undef STEP - -// Like Fls64() above, but returns the 0-based position of the last set bit -// (i.e., most significant bit) in the given uint128. The argument may not be 0. -static inline int Fls128(uint128 n) { +inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 n) { if (uint64_t hi = Uint128High64(n)) { - return Fls64(hi) + 64; + ABSL_INTERNAL_ASSUME(hi != 0); + return 127 - base_internal::CountLeadingZeros64(hi); } - return Fls64(Uint128Low64(n)); + const uint64_t low = Uint128Low64(n); + ABSL_INTERNAL_ASSUME(low != 0); + return 63 - base_internal::CountLeadingZeros64(low); } // Long division/modulo for uint128 implemented using the shift-subtract // division algorithm adapted from: // https://stackoverflow.com/questions/5386377/division-without-using -void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, - uint128* remainder_ret) { +inline void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, + uint128* remainder_ret) { assert(divisor != 0); if (divisor > dividend) {
diff --git a/absl/numeric/int128_benchmark.cc b/absl/numeric/int128_benchmark.cc index a5502d9..eab1515 100644 --- a/absl/numeric/int128_benchmark.cc +++ b/absl/numeric/int128_benchmark.cc
@@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/numeric/int128.h" - #include <algorithm> #include <cstdint> +#include <limits> #include <random> #include <vector> #include "benchmark/benchmark.h" #include "absl/base/config.h" +#include "absl/numeric/int128.h" namespace { @@ -32,57 +32,85 @@ return std::mt19937(seed); } -std::vector<std::pair<absl::uint128, absl::uint128>> -GetRandomClass128SampleUniformDivisor() { - std::vector<std::pair<absl::uint128, absl::uint128>> values; +template <typename T, + typename H = typename std::conditional< + std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type> +std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() { + std::vector<std::pair<T, T>> values; std::mt19937 random = MakeRandomEngine(); - std::uniform_int_distribution<uint64_t> uniform_uint64; + std::uniform_int_distribution<H> uniform_h; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - absl::uint128 a = - absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); - absl::uint128 b = - absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); - values.emplace_back(std::max(a, b), - std::max(absl::uint128(2), std::min(a, b))); + T a{absl::MakeUint128(uniform_h(random), uniform_h(random))}; + T b{absl::MakeUint128(uniform_h(random), uniform_h(random))}; + values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b))); } return values; } +template <typename T> void BM_DivideClass128UniformDivisor(benchmark::State& state) { - auto values = GetRandomClass128SampleUniformDivisor(); + auto values = GetRandomClass128SampleUniformDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideClass128UniformDivisor); +BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::int128); -std::vector<std::pair<absl::uint128, uint64_t>> -GetRandomClass128SampleSmallDivisor() { - std::vector<std::pair<absl::uint128, uint64_t>> values; +template <typename T> +void BM_RemainderClass128UniformDivisor(benchmark::State& state) { + auto values = GetRandomClass128SampleUniformDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::int128); + +template <typename T, + typename H = typename std::conditional< + std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type> +std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() { + std::vector<std::pair<T, H>> values; std::mt19937 random = MakeRandomEngine(); - std::uniform_int_distribution<uint64_t> uniform_uint64; + std::uniform_int_distribution<H> uniform_h; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - absl::uint128 a = - absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); - uint64_t b = std::max(uint64_t{2}, uniform_uint64(random)); - values.emplace_back(std::max(a, absl::uint128(b)), b); + T a{absl::MakeUint128(uniform_h(random), uniform_h(random))}; + H b{std::max(H{2}, uniform_h(random))}; + values.emplace_back(std::max(a, T(b)), b); } return values; } +template <typename T> void BM_DivideClass128SmallDivisor(benchmark::State& state) { - auto values = GetRandomClass128SampleSmallDivisor(); + auto values = GetRandomClass128SampleSmallDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideClass128SmallDivisor); +BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::int128); + +template <typename T> +void BM_RemainderClass128SmallDivisor(benchmark::State& state) { + auto values = GetRandomClass128SampleSmallDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::int128); std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() { std::vector<std::pair<absl::uint128, absl::uint128>> values; @@ -121,74 +149,107 @@ // Some implementations of <random> do not support __int128 when it is // available, so we make our own uniform_int_distribution-like type. +template <typename T, + typename H = typename std::conditional< + std::is_same<T, __int128>::value, int64_t, uint64_t>::type> class UniformIntDistribution128 { public: // NOLINTNEXTLINE: mimicking std::uniform_int_distribution API - unsigned __int128 operator()(std::mt19937& generator) { - return (static_cast<unsigned __int128>(dist64_(generator)) << 64) | - dist64_(generator); + T operator()(std::mt19937& generator) { + return (static_cast<T>(dist64_(generator)) << 64) | dist64_(generator); } private: - std::uniform_int_distribution<uint64_t> dist64_; + std::uniform_int_distribution<H> dist64_; }; -std::vector<std::pair<unsigned __int128, unsigned __int128>> -GetRandomIntrinsic128SampleUniformDivisor() { - std::vector<std::pair<unsigned __int128, unsigned __int128>> values; +template <typename T, + typename H = typename std::conditional< + std::is_same<T, __int128>::value, int64_t, uint64_t>::type> +std::vector<std::pair<T, T>> GetRandomIntrinsic128SampleUniformDivisor() { + std::vector<std::pair<T, T>> values; std::mt19937 random = MakeRandomEngine(); - UniformIntDistribution128 uniform_uint128; + UniformIntDistribution128<T> uniform_128; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - unsigned __int128 a = uniform_uint128(random); - unsigned __int128 b = uniform_uint128(random); - values.emplace_back( - std::max(a, b), - std::max(static_cast<unsigned __int128>(2), std::min(a, b))); + T a = uniform_128(random); + T b = uniform_128(random); + values.emplace_back(std::max(a, b), + std::max(static_cast<T>(2), std::min(a, b))); } return values; } +template <typename T> void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) { - auto values = GetRandomIntrinsic128SampleUniformDivisor(); + auto values = GetRandomIntrinsic128SampleUniformDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideIntrinsic128UniformDivisor); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, __int128); -std::vector<std::pair<unsigned __int128, uint64_t>> -GetRandomIntrinsic128SampleSmallDivisor() { - std::vector<std::pair<unsigned __int128, uint64_t>> values; +template <typename T> +void BM_RemainderIntrinsic128UniformDivisor(benchmark::State& state) { + auto values = GetRandomIntrinsic128SampleUniformDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, __int128); + +template <typename T, + typename H = typename std::conditional< + std::is_same<T, __int128>::value, int64_t, uint64_t>::type> +std::vector<std::pair<T, H>> GetRandomIntrinsic128SampleSmallDivisor() { + std::vector<std::pair<T, H>> values; std::mt19937 random = MakeRandomEngine(); - UniformIntDistribution128 uniform_uint128; - std::uniform_int_distribution<uint64_t> uniform_uint64; + UniformIntDistribution128<T> uniform_int128; + std::uniform_int_distribution<H> uniform_int64; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - unsigned __int128 a = uniform_uint128(random); - uint64_t b = std::max(uint64_t{2}, uniform_uint64(random)); - values.emplace_back(std::max(a, static_cast<unsigned __int128>(b)), b); + T a = uniform_int128(random); + H b = std::max(H{2}, uniform_int64(random)); + values.emplace_back(std::max(a, static_cast<T>(b)), b); } return values; } +template <typename T> void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) { - auto values = GetRandomIntrinsic128SampleSmallDivisor(); + auto values = GetRandomIntrinsic128SampleSmallDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideIntrinsic128SmallDivisor); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, __int128); + +template <typename T> +void BM_RemainderIntrinsic128SmallDivisor(benchmark::State& state) { + auto values = GetRandomIntrinsic128SampleSmallDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, __int128); std::vector<std::pair<unsigned __int128, unsigned __int128>> GetRandomIntrinsic128Sample() { std::vector<std::pair<unsigned __int128, unsigned __int128>> values; std::mt19937 random = MakeRandomEngine(); - UniformIntDistribution128 uniform_uint128; + UniformIntDistribution128<unsigned __int128> uniform_uint128; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { values.emplace_back(uniform_uint128(random), uniform_uint128(random));
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 8875b30..ade9ea1 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt
@@ -69,6 +69,21 @@ # Internal-only target, do not depend on directly. absl_cc_library( NAME + random_internal_mock_helpers + HDRS + "internal/mock_helpers.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::fast_type_id + absl::optional +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME random_internal_mock_overload_set HDRS "internal/mock_overload_set.h" @@ -78,7 +93,7 @@ ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_mocking_bit_gen - absl::fast_type_id + absl::random_internal_mock_helpers TESTONLY )
diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h index 00e3624..9555460 100644 --- a/absl/random/bit_gen_ref.h +++ b/absl/random/bit_gen_ref.h
@@ -53,6 +53,7 @@ template <typename> struct DistributionCaller; +class MockHelpers; } // namespace random_internal @@ -171,6 +172,7 @@ template <typename> friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for InvokeMock }; ABSL_NAMESPACE_END
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 3ab1177..d81477f 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel
@@ -485,11 +485,20 @@ ) cc_library( + name = "mock_helpers", + hdrs = ["mock_helpers.h"], + deps = [ + "//absl/base:fast_type_id", + "//absl/types:optional", + ], +) + +cc_library( name = "mock_overload_set", testonly = 1, hdrs = ["mock_overload_set.h"], deps = [ - "//absl/base:fast_type_id", + ":mock_helpers", "//absl/random:mocking_bit_gen", "@com_google_googletest//:gtest", ],
diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h index 87a7684..fc81b78 100644 --- a/absl/random/internal/distribution_caller.h +++ b/absl/random/internal/distribution_caller.h
@@ -52,23 +52,25 @@ // Default implementation of distribution caller. template <typename DistrT, typename... Args> - static inline typename DistrT::result_type Impl(std::false_type, URBG* urbg, - Args&&... args) { + static typename DistrT::result_type Impl(std::false_type, URBG* urbg, + Args&&... args) { DistrT dist(std::forward<Args>(args)...); return dist(*urbg); } // Mock implementation of distribution caller. + // The underlying KeyT must match the KeyT constructed by MockOverloadSet. template <typename DistrT, typename... Args> - static inline typename DistrT::result_type Impl(std::true_type, URBG* urbg, - Args&&... args) { + static typename DistrT::result_type Impl(std::true_type, URBG* urbg, + Args&&... args) { using ResultT = typename DistrT::result_type; using ArgTupleT = std::tuple<absl::decay_t<Args>...>; + using KeyT = ResultT(DistrT, ArgTupleT); + ArgTupleT arg_tuple(std::forward<Args>(args)...); ResultT result; - if (!urbg->InvokeMock( - ::absl::base_internal::FastTypeId<ResultT(DistrT, ArgTupleT)>(), - &arg_tuple, &result)) { + if (!urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple, + &result)) { auto dist = absl::make_from_tuple<DistrT>(arg_tuple); result = dist(*urbg); } @@ -77,7 +79,7 @@ // Default implementation of distribution caller. template <typename DistrT, typename... Args> - static inline typename DistrT::result_type Call(URBG* urbg, Args&&... args) { + static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { return Impl<DistrT, Args...>(HasInvokeMock{}, urbg, std::forward<Args>(args)...); }
diff --git a/absl/random/internal/mock_helpers.h b/absl/random/internal/mock_helpers.h new file mode 100644 index 0000000..9af27ab --- /dev/null +++ b/absl/random/internal/mock_helpers.h
@@ -0,0 +1,127 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_ +#define ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_ + +#include <tuple> +#include <type_traits> + +#include "absl/base/internal/fast_type_id.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +// MockHelpers works in conjunction with MockOverloadSet, MockingBitGen, and +// BitGenRef to enable the mocking capability for absl distribution functions. +// +// MockingBitGen registers mocks based on the typeid of a mock signature, KeyT, +// which is used to generate a unique id. +// +// KeyT is a signature of the form: +// result_type(discriminator_type, std::tuple<args...>) +// The mocked function signature will be composed from KeyT as: +// result_type(args...) +// +class MockHelpers { + using IdType = ::absl::base_internal::FastTypeIdType; + + // Given a key signature type used to index the mock, extract the components. + // KeyT is expected to have the form: + // result_type(discriminator_type, arg_tuple_type) + template <typename KeyT> + struct KeySignature; + + template <typename ResultT, typename DiscriminatorT, typename ArgTupleT> + struct KeySignature<ResultT(DiscriminatorT, ArgTupleT)> { + using result_type = ResultT; + using discriminator_type = DiscriminatorT; + using arg_tuple_type = ArgTupleT; + }; + + // Detector for InvokeMock. + template <class T> + using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( + std::declval<IdType>(), std::declval<void*>(), std::declval<void*>())); + + // Empty implementation of InvokeMock. + template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG, + typename... Args> + static absl::optional<ReturnT> InvokeMockImpl(char, URBG*, Args&&...) { + return absl::nullopt; + } + + // Non-empty implementation of InvokeMock. + template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG, + typename = invoke_mock_t<URBG>, typename... Args> + static absl::optional<ReturnT> InvokeMockImpl(int, URBG* urbg, + Args&&... args) { + ArgTupleT arg_tuple(std::forward<Args>(args)...); + ReturnT result; + if (urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple, + &result)) { + return result; + } + return absl::nullopt; + } + + public: + // Invoke a mock for the KeyT (may or may not be a signature). + // + // KeyT is used to generate a typeid-based lookup key for the mock. + // KeyT is a signature of the form: + // result_type(discriminator_type, std::tuple<args...>) + // The mocked function signature will be composed from KeyT as: + // result_type(args...) + // + // An instance of arg_tuple_type must be constructable from Args..., since + // the underlying mechanism requires a pointer to an argument tuple. + template <typename KeyT, typename URBG, typename... Args> + static auto MaybeInvokeMock(URBG* urbg, Args&&... args) + -> absl::optional<typename KeySignature<KeyT>::result_type> { + // Use function overloading to dispatch to the implemenation since + // more modern patterns (e.g. require + constexpr) are not supported in all + // compiler configurations. + return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type, + typename KeySignature<KeyT>::arg_tuple_type, URBG>( + 0, urbg, std::forward<Args>(args)...); + } + + // Acquire a mock for the KeyT (may or may not be a signature). + // + // KeyT is used to generate a typeid-based lookup for the mock. + // KeyT is a signature of the form: + // result_type(discriminator_type, std::tuple<args...>) + // The mocked function signature will be composed from KeyT as: + // result_type(args...) + template <typename KeyT, typename MockURBG> + static auto MockFor(MockURBG& m) -> decltype( + std::declval<MockURBG>() + .template RegisterMock<typename KeySignature<KeyT>::result_type, + typename KeySignature<KeyT>::arg_tuple_type>( + std::declval<IdType>())) { + return m.template RegisterMock<typename KeySignature<KeyT>::result_type, + typename KeySignature<KeyT>::arg_tuple_type>( + ::absl::base_internal::FastTypeId<KeyT>()); + } +}; + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h index 60561b5..dccc6ce 100644 --- a/absl/random/internal/mock_overload_set.h +++ b/absl/random/internal/mock_overload_set.h
@@ -20,7 +20,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "absl/base/internal/fast_type_id.h" +#include "absl/random/internal/mock_helpers.h" #include "absl/random/mocking_bit_gen.h" namespace absl { @@ -37,22 +37,19 @@ // `mock_single_overload.gmock_Call(...)`. Because expectations are stored on // the MockingBitGen (an argument passed inside `Call(...)`), this forwards to // arguments to MockingBitGen::Register. +// +// The underlying KeyT must match the KeyT constructed by DistributionCaller. template <typename DistrT, typename Ret, typename... Args> struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> { static_assert(std::is_same<typename DistrT::result_type, Ret>::value, "Overload signature must have return type matching the " "distribution result_type."); - using ArgTupleT = std::tuple<Args...>; + using KeyT = Ret(DistrT, std::tuple<Args...>); auto gmock_Call( absl::MockingBitGen& gen, // NOLINT(google-runtime-references) const ::testing::Matcher<Args>&... matchers) - -> decltype(gen.RegisterMock<Ret, ArgTupleT>( - std::declval<::absl::base_internal::FastTypeIdType>()) - .gmock_Call(matchers...)) { - return gen - .RegisterMock<Ret, ArgTupleT>( - ::absl::base_internal::FastTypeId<Ret(DistrT, ArgTupleT)>()) - .gmock_Call(matchers...); + -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...)) { + return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...); } }; @@ -61,18 +58,14 @@ static_assert(std::is_same<typename DistrT::result_type, Ret>::value, "Overload signature must have return type matching the " "distribution result_type."); - using ArgTupleT = std::tuple<Arg, Args...>; + using KeyT = Ret(DistrT, std::tuple<Arg, Args...>); auto gmock_Call( const ::testing::Matcher<Arg>& matcher, absl::MockingBitGen& gen, // NOLINT(google-runtime-references) const ::testing::Matcher<Args>&... matchers) - -> decltype(gen.RegisterMock<Ret, ArgTupleT>( - std::declval<::absl::base_internal::FastTypeIdType>()) - .gmock_Call(matcher, matchers...)) { - return gen - .RegisterMock<Ret, ArgTupleT>( - ::absl::base_internal::FastTypeId<Ret(DistrT, ArgTupleT)>()) - .gmock_Call(matcher, matchers...); + -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, + matchers...)) { + return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, matchers...); } };
diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h index c3c7d44..6d2f2c8 100644 --- a/absl/random/mocking_bit_gen.h +++ b/absl/random/mocking_bit_gen.h
@@ -53,10 +53,9 @@ ABSL_NAMESPACE_BEGIN namespace random_internal { -template <typename, typename> -struct MockSingleOverload; template <typename> struct DistributionCaller; +class MockHelpers; } // namespace random_internal class BitGenRef; @@ -216,11 +215,11 @@ std::vector<std::function<void()>> deleters_; absl::BitGen gen_; - template <typename, typename> - friend struct ::absl::random_internal::MockSingleOverload; // for Register template <typename> friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock friend class ::absl::BitGenRef; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for RegisterMock, + // InvokeMock }; ABSL_NAMESPACE_END
diff --git a/absl/random/seed_sequences_test.cc b/absl/random/seed_sequences_test.cc index 2cc8b0e..fe1100b 100644 --- a/absl/random/seed_sequences_test.cc +++ b/absl/random/seed_sequences_test.cc
@@ -96,7 +96,6 @@ void TestReproducibleVariateSequencesForNonsecureURBG() { const size_t kNumVariates = 1000; - // Master RNG instance. URBG rng; // Reused for both RNG instances. auto reusable_seed = absl::CreateSeedSeqFrom(&rng);
diff --git a/absl/status/status.cc b/absl/status/status.cc index 6d57a6b..0a65573 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc
@@ -27,8 +27,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// The implementation was intentionally kept same as util::error::Code_Name() -// to ease the migration. std::string StatusCodeToString(StatusCode code) { switch (code) { case StatusCode::kOk:
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index dc98745..8580d80 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -857,16 +857,16 @@ struct Rank1 {}; struct Rank0 : Rank1 {}; -template <typename Releaser, typename = ::absl::base_internal::InvokeT< +template <typename Releaser, typename = ::absl::base_internal::invoke_result_t< Releaser, absl::string_view>> void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) { - ::absl::base_internal::Invoke(std::forward<Releaser>(releaser), data); + ::absl::base_internal::invoke(std::forward<Releaser>(releaser), data); } template <typename Releaser, - typename = ::absl::base_internal::InvokeT<Releaser>> + typename = ::absl::base_internal::invoke_result_t<Releaser>> void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) { - ::absl::base_internal::Invoke(std::forward<Releaser>(releaser)); + ::absl::base_internal::invoke(std::forward<Releaser>(releaser)); } // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index d441e87..3dbc152 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h
@@ -25,10 +25,12 @@ class FormatCountCapture; class FormatSink; -namespace str_format_internal { - +template <absl::FormatConversionCharSet C> +struct FormatConvertResult; class FormatConversionSpec; +namespace str_format_internal { + template <typename T, typename = void> struct HasUserDefinedConvert : std::false_type {}; @@ -39,6 +41,22 @@ std::declval<FormatSink*>()))>> : std::true_type {}; +void AbslFormatConvert(); // Stops the lexical name lookup +template <typename T> +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) + -> decltype(AbslFormatConvert(v, + std::declval<const FormatConversionSpec&>(), + std::declval<FormatSink*>())) { + using FormatConversionSpecT = + absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; + using FormatSinkT = + absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + auto fcs = conv.Wrap<FormatConversionSpecT>(); + auto fs = sink->Wrap<FormatSinkT>(); + return AbslFormatConvert(v, fcs, &fs); +} + template <typename T> class StreamedWrapper; @@ -46,6 +64,13 @@ // then convert it, appending to `sink` and return `true`. // Otherwise fail and return `false`. +// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' +// as an extension mechanism. These FormatConvertImpl functions are the default +// implementations. +// The ADL search is augmented via the 'Sink*' parameter, which also +// serves as a disambiguator to reject possible unintended 'AbslFormatConvert' +// functions in the namespaces associated with 'v'. + // Raw pointers. struct VoidPtr { VoidPtr() = default; @@ -62,6 +87,11 @@ }; template <FormatConversionCharSet C> +constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { + return C; +} + +template <FormatConversionCharSet C> constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { return C; }
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc index bf3d7e8..f53fd6b 100644 --- a/absl/strings/internal/str_format/arg_test.cc +++ b/absl/strings/internal/str_format/arg_test.cc
@@ -23,8 +23,17 @@ enum Color { kRed, kGreen, kBlue }; static const char *hi() { return "hi"; } + + struct X {}; + + X x_; }; +inline FormatConvertResult<FormatConversionCharSet{}> AbslFormatConvert( + const FormatArgImplTest::X &, const FormatConversionSpec &, FormatSink *) { + return {false}; +} + TEST_F(FormatArgImplTest, ToInt) { int out = 0; EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out)); @@ -59,6 +68,7 @@ FormatArgImpl(static_cast<int *>(nullptr)), &out)); EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out)); EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out)); + EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(x_), &out)); EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out)); EXPECT_EQ(2, out); }
diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc index 94f2b9c..bb0d96c 100644 --- a/absl/strings/internal/str_format/extension.cc +++ b/absl/strings/internal/str_format/extension.cc
@@ -33,6 +33,29 @@ return s; } +#define ABSL_INTERNAL_X_VAL(id) \ + constexpr absl::FormatConversionChar FormatConversionCharInternal::id; +ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr absl::FormatConversionChar FormatConversionCharInternal::kNone; + +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + constexpr FormatConversionCharSet FormatConversionCharSetInternal::c; +ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) +#undef ABSL_INTERNAL_CHAR_SET_CASE + +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer; + bool FormatSinkImpl::PutPaddedString(string_view value, int width, int precision, bool left) { size_t space_remaining = 0;
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index 6c60c6c..a9b9e13 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h
@@ -31,11 +31,11 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - enum class FormatConversionChar : uint8_t; enum class FormatConversionCharSet : uint64_t; +namespace str_format_internal { + class FormatRawSinkImpl { public: // Implicitly convert from any type that provides the hook function as @@ -361,14 +361,12 @@ static constexpr FormatConversionCharSet kStar = FormatConversionCharToConvValue('*'); - // Some predefined values (TODO(matthewbr), delete any that are unused). static constexpr FormatConversionCharSet kIntegral = FormatConversionCharSetUnion(d, i, u, o, x, X); static constexpr FormatConversionCharSet kFloating = FormatConversionCharSetUnion(a, e, f, g, A, E, F, G); static constexpr FormatConversionCharSet kNumeric = FormatConversionCharSetUnion(kIntegral, kFloating); - static constexpr FormatConversionCharSet kString = s; static constexpr FormatConversionCharSet kPointer = p; };
diff --git a/absl/strings/internal/str_format/extension_test.cc b/absl/strings/internal/str_format/extension_test.cc index 0a023f9..1c93fdb 100644 --- a/absl/strings/internal/str_format/extension_test.cc +++ b/absl/strings/internal/str_format/extension_test.cc
@@ -80,4 +80,19 @@ EXPECT_EQ(actual, expected); } } + +TEST(FormatExtensionTest, VerifyEnumEquality) { +#define X_VAL(id) \ + EXPECT_EQ(absl::FormatConversionChar::id, \ + absl::str_format_internal::FormatConversionCharInternal::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, ); +#undef X_VAL + +#define X_VAL(id) \ + EXPECT_EQ(absl::FormatConversionCharSet::id, \ + absl::str_format_internal::FormatConversionCharSetInternal::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, ); +#undef X_VAL +} + } // namespace
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index 36bd84a..0146510 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h
@@ -63,6 +63,9 @@ // loosely typed. `FormatUntyped()` is not a template and does not perform // any compile-time checking of the format string; instead, it returns a // boolean from a runtime check. +// +// In addition, the `str_format` library provides extension points for +// augmenting formatting to new types. See "StrFormat Extensions" below. #ifndef ABSL_STRINGS_STR_FORMAT_H_ #define ABSL_STRINGS_STR_FORMAT_H_ @@ -278,9 +281,36 @@ // } else { // ... error case ... // } + +#if defined(__cpp_nontype_template_parameter_auto) +// If C++17 is available, an 'extended' format is also allowed that can specify +// multiple conversion characters per format argument, using a combination of +// `absl::FormatConversionCharSet` enum values (logically a set union) +// via the `|` operator. (Single character-based arguments are still accepted, +// but cannot be combined). Some common conversions also have predefined enum +// values, such as `absl::FormatConversionCharSet::kIntegral`. +// +// Example: +// // Extended format supports multiple conversion characters per argument, +// // specified via a combination of `FormatConversionCharSet` enums. +// using MyFormat = absl::ParsedFormat<absl::FormatConversionCharSet::d | +// absl::FormatConversionCharSet::x>; +// MyFormat GetFormat(bool use_hex) { +// if (use_hex) return MyFormat("foo %x bar"); +// return MyFormat("foo %d bar"); +// } +// // `format` can be used with any value that supports 'd' and 'x', +// // like `int`. +// auto format = GetFormat(use_hex); +// value = StringF(format, i); +template <auto... Conv> +using ParsedFormat = absl::str_format_internal::ExtendedParsedFormat< + absl::str_format_internal::ToFormatConversionCharSet(Conv)...>; +#else template <char... Conv> using ParsedFormat = str_format_internal::ExtendedParsedFormat< absl::str_format_internal::ToFormatConversionCharSet(Conv)...>; +#endif // defined(__cpp_nontype_template_parameter_auto) // StrFormat() // @@ -537,6 +567,246 @@ str_format_internal::UntypedFormatSpecImpl::Extract(format), args); } +//------------------------------------------------------------------------------ +// StrFormat Extensions +//------------------------------------------------------------------------------ +// +// AbslFormatConvert() +// +// The StrFormat library provides a customization API for formatting +// user-defined types using absl::StrFormat(). The API relies on detecting an +// overload in the user-defined type's namespace of a free (non-member) +// `AbslFormatConvert()` function, usually as a friend definition with the +// following signature: +// +// absl::FormatConvertResult<...> AbslFormatConvert( +// const X& value, +// const absl::FormatConversionSpec& spec, +// absl::FormatSink *sink); +// +// An `AbslFormatConvert()` overload for a type should only be declared in the +// same file and namespace as said type. +// +// The abstractions within this definition include: +// +// * An `absl::FormatConversionSpec` to specify the fields to pull from a +// user-defined type's format string +// * An `absl::FormatSink` to hold the converted string data during the +// conversion process. +// * An `absl::FormatConvertResult` to hold the status of the returned +// formatting operation +// +// The return type encodes all the conversion characters that your +// AbslFormatConvert() routine accepts. The return value should be {true}. +// A return value of {false} will result in `StrFormat()` returning +// an empty string. This result will be propagated to the result of +// `FormatUntyped`. +// +// Example: +// +// struct Point { +// // To add formatting support to `Point`, we simply need to add a free +// // (non-member) function `AbslFormatConvert()`. This method interprets +// // `spec` to print in the request format. The allowed conversion characters +// // can be restricted via the type of the result, in this example +// // string and integral formatting are allowed (but not, for instance +// // floating point characters like "%f"). You can add such a free function +// // using a friend declaration within the body of the class: +// friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString | +// absl::FormatConversionCharSet::kIntegral> +// AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec, +// absl::FormatSink* s) { +// if (spec.conversion_char() == absl::FormatConversionChar::s) { +// s->Append(absl::StrCat("x=", p.x, " y=", p.y)); +// } else { +// s->Append(absl::StrCat(p.x, ",", p.y)); +// } +// return {true}; +// } +// +// int x; +// int y; +// }; + +// clang-format off + +// FormatConversionChar +// +// Specifies the formatting character provided in the format string +// passed to `StrFormat()`. +enum class FormatConversionChar : uint8_t { + c, s, // text + d, i, o, u, x, X, // int + f, F, e, E, g, G, a, A, // float + n, p // misc +}; +// clang-format on + +// FormatConversionSpec +// +// Specifies modifications to the conversion of the format string, through use +// of one or more format flags in the source format string. +class FormatConversionSpec { + public: + // FormatConversionSpec::is_basic() + // + // Indicates that width and precision are not specified, and no additional + // flags are set for this conversion character in the format string. + bool is_basic() const { return impl_.is_basic(); } + + // FormatConversionSpec::has_left_flag() + // + // Indicates whether the result should be left justified for this conversion + // character in the format string. This flag is set through use of a '-' + // character in the format string. E.g. "%-s" + bool has_left_flag() const { return impl_.has_left_flag(); } + + // FormatConversionSpec::has_show_pos_flag() + // + // Indicates whether a sign column is prepended to the result for this + // conversion character in the format string, even if the result is positive. + // This flag is set through use of a '+' character in the format string. + // E.g. "%+d" + bool has_show_pos_flag() const { return impl_.has_show_pos_flag(); } + + // FormatConversionSpec::has_sign_col_flag() + // + // Indicates whether a mandatory sign column is added to the result for this + // conversion character. This flag is set through use of a space character + // (' ') in the format string. E.g. "% i" + bool has_sign_col_flag() const { return impl_.has_sign_col_flag(); } + + // FormatConversionSpec::has_alt_flag() + // + // Indicates whether an "alternate" format is applied to the result for this + // conversion character. Alternative forms depend on the type of conversion + // character, and unallowed alternatives are undefined. This flag is set + // through use of a '#' character in the format string. E.g. "%#h" + bool has_alt_flag() const { return impl_.has_alt_flag(); } + + // FormatConversionSpec::has_zero_flag() + // + // Indicates whether zeroes should be prepended to the result for this + // conversion character instead of spaces. This flag is set through use of the + // '0' character in the format string. E.g. "%0f" + bool has_zero_flag() const { return impl_.has_zero_flag(); } + + // FormatConversionSpec::conversion_char() + // + // Returns the underlying conversion character. + FormatConversionChar conversion_char() const { + return impl_.conversion_char(); + } + + // FormatConversionSpec::width() + // + // Returns the specified width (indicated through use of a non-zero integer + // value or '*' character) of the conversion character. If width is + // unspecified, it returns a negative value. + int width() const { return impl_.width(); } + + // FormatConversionSpec::precision() + // + // Returns the specified precision (through use of the '.' character followed + // by a non-zero integer value or '*' character) of the conversion character. + // If precision is unspecified, it returns a negative value. + int precision() const { return impl_.precision(); } + + private: + explicit FormatConversionSpec( + str_format_internal::FormatConversionSpecImpl impl) + : impl_(impl) {} + + friend str_format_internal::FormatConversionSpecImpl; + + absl::str_format_internal::FormatConversionSpecImpl impl_; +}; + +// Type safe OR operator for FormatConversionCharSet to allow accepting multiple +// conversion chars in custom format converters. +constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, + FormatConversionCharSet b) { + return static_cast<FormatConversionCharSet>(static_cast<uint64_t>(a) | + static_cast<uint64_t>(b)); +} + +// FormatConversionCharSet +// +// Specifies the _accepted_ conversion types as a template parameter to +// FormatConvertResult for custom implementations of `AbslFormatConvert`. +// Note the helper predefined alias definitions (kIntegral, etc.) below. +enum class FormatConversionCharSet : uint64_t { + // text + c = str_format_internal::FormatConversionCharToConvInt('c'), + s = str_format_internal::FormatConversionCharToConvInt('s'), + // integer + d = str_format_internal::FormatConversionCharToConvInt('d'), + i = str_format_internal::FormatConversionCharToConvInt('i'), + o = str_format_internal::FormatConversionCharToConvInt('o'), + u = str_format_internal::FormatConversionCharToConvInt('u'), + x = str_format_internal::FormatConversionCharToConvInt('x'), + X = str_format_internal::FormatConversionCharToConvInt('X'), + // Float + f = str_format_internal::FormatConversionCharToConvInt('f'), + F = str_format_internal::FormatConversionCharToConvInt('F'), + e = str_format_internal::FormatConversionCharToConvInt('e'), + E = str_format_internal::FormatConversionCharToConvInt('E'), + g = str_format_internal::FormatConversionCharToConvInt('g'), + G = str_format_internal::FormatConversionCharToConvInt('G'), + a = str_format_internal::FormatConversionCharToConvInt('a'), + A = str_format_internal::FormatConversionCharToConvInt('A'), + // misc + n = str_format_internal::FormatConversionCharToConvInt('n'), + p = str_format_internal::FormatConversionCharToConvInt('p'), + + // Used for width/precision '*' specification. + kStar = static_cast<uint64_t>( + absl::str_format_internal::FormatConversionCharSetInternal::kStar), + // Some predefined values: + kIntegral = d | i | u | o | x | X, + kFloating = a | e | f | g | A | E | F | G, + kNumeric = kIntegral | kFloating, + kString = s, + kPointer = p, +}; + +// FormatSink +// +// An abstraction to which conversions write their string data. +// +class FormatSink { + public: + // Appends `count` copies of `ch`. + void Append(size_t count, char ch) { sink_->Append(count, ch); } + + void Append(string_view v) { sink_->Append(v); } + + // Appends the first `precision` bytes of `v`. If this is less than + // `width`, spaces will be appended first (if `left` is false), or + // after (if `left` is true) to ensure the total amount appended is + // at least `width`. + bool PutPaddedString(string_view v, int width, int precision, bool left) { + return sink_->PutPaddedString(v, width, precision, left); + } + + private: + friend str_format_internal::FormatSinkImpl; + explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {} + str_format_internal::FormatSinkImpl* sink_; +}; + +// FormatConvertResult +// +// Indicates whether a call to AbslFormatConvert() was successful. +// This return type informs the StrFormat extension framework (through +// ADL but using the return type) of what conversion characters are supported. +// It is strongly discouraged to return {false}, as this will result in an +// empty string in StrFormat. +template <FormatConversionCharSet C> +struct FormatConvertResult { + bool value; +}; + ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index 22cfef6..d9fb25a 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc
@@ -16,7 +16,6 @@ ABSL_NAMESPACE_BEGIN namespace { using str_format_internal::FormatArgImpl; -using str_format_internal::FormatConversionCharSetInternal; using FormatEntryPointTest = ::testing::Test; @@ -537,46 +536,90 @@ EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format))); } -using absl::str_format_internal::FormatConversionCharSet; +#if defined(__cpp_nontype_template_parameter_auto) + +template <auto T> +std::true_type IsValidParsedFormatArgTest(ParsedFormat<T>*); + +template <auto T> +std::false_type IsValidParsedFormatArgTest(...); + +template <auto T> +using IsValidParsedFormatArg = decltype(IsValidParsedFormatArgTest<T>(nullptr)); + +TEST_F(ParsedFormatTest, OnlyValidTypesAllowed) { + ASSERT_TRUE(IsValidParsedFormatArg<'c'>::value); + + ASSERT_TRUE(IsValidParsedFormatArg<FormatConversionCharSet::d>::value); + + ASSERT_TRUE(IsValidParsedFormatArg<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::x>::value); + ASSERT_TRUE( + IsValidParsedFormatArg<absl::FormatConversionCharSet::kIntegral>::value); + + // This is an easy mistake to make, however, this will reduce to an integer + // which has no meaning, so we need to ensure it doesn't compile. + ASSERT_FALSE(IsValidParsedFormatArg<'x' | 'd'>::value); + + // For now, we disallow construction based on ConversionChar (rather than + // CharSet) + ASSERT_FALSE(IsValidParsedFormatArg<absl::FormatConversionChar::d>::value); +} + +TEST_F(ParsedFormatTest, ExtendedTyping) { + EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New("")); + ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d")); + auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::s>::New("%d%s"); + ASSERT_TRUE(v1); + auto v2 = ParsedFormat<absl::FormatConversionCharSet::d, 's'>::New("%d%s"); + ASSERT_TRUE(v2); + auto v3 = ParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::s, + 's'>::New("%d%s"); + ASSERT_TRUE(v3); + auto v4 = ParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::s, + 's'>::New("%s%s"); + ASSERT_TRUE(v4); +} +#endif TEST_F(ParsedFormatTest, UncheckedCorrect) { auto f = - ExtendedParsedFormat<FormatConversionCharSetInternal::d>::New("ABC%dDEF"); + ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); std::string format = "%sFFF%dZZZ%f"; auto f2 = ExtendedParsedFormat< - FormatConversionCharSetInternal::kString, - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::kFloating>::New(format); + absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::kFloating>::New(format); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2)); f2 = ExtendedParsedFormat< - FormatConversionCharSetInternal::kString, - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::kFloating>::New("%s %d %f"); + absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::kFloating>::New("%s %d %f"); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2)); auto star = - ExtendedParsedFormat<FormatConversionCharSetInternal::kStar, - FormatConversionCharSetInternal::d>::New("%*d"); + ExtendedParsedFormat<absl::FormatConversionCharSet::kStar, + absl::FormatConversionCharSet::d>::New("%*d"); ASSERT_TRUE(star); EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star)); - auto dollar = ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::New("%2$s %1$d"); + auto dollar = + ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("%2$s %1$d"); ASSERT_TRUE(dollar); EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar)); // with reuse dollar = ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::New("%2$s %1$d %1$d"); + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("%2$s %1$d %1$d"); ASSERT_TRUE(dollar); EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar)); @@ -584,62 +627,61 @@ TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) { EXPECT_FALSE( - (ExtendedParsedFormat<FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::New("ABC"))); + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("ABC"))); EXPECT_FALSE( - (ExtendedParsedFormat<FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::New("%dABC"))); - EXPECT_FALSE((ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::New("ABC%2$s"))); + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("%dABC"))); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("ABC%2$s"))); auto f = ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::NewAllowIgnored("ABC"); + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f)); f = ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::NewAllowIgnored("%dABC"); + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::NewAllowIgnored("%dABC"); ASSERT_TRUE(f); EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f)); f = ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::s>::NewAllowIgnored("ABC%2$s"); + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC%2$s"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f)); } TEST_F(ParsedFormatTest, UncheckedMultipleTypes) { - auto dx = ExtendedParsedFormat< - FormatConversionCharSetInternal::d | - FormatConversionCharSetInternal::x>::New("%1$d %1$x"); + auto dx = + ExtendedParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::x>::New("%1$d %1$x"); EXPECT_TRUE(dx); EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx)); - dx = ExtendedParsedFormat<FormatConversionCharSetInternal::d | - FormatConversionCharSetInternal::x>::New("%1$d"); + dx = ExtendedParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::x>::New("%1$d"); EXPECT_TRUE(dx); EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx)); } TEST_F(ParsedFormatTest, UncheckedIncorrect) { - EXPECT_FALSE( - ExtendedParsedFormat<FormatConversionCharSetInternal::d>::New("")); + EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("")); - EXPECT_FALSE(ExtendedParsedFormat<FormatConversionCharSetInternal::d>::New( + EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New( "ABC%dDEF%d")); std::string format = "%sFFF%dZZZ%f"; EXPECT_FALSE( - (ExtendedParsedFormat<FormatConversionCharSetInternal::s, - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::g>::New(format))); + (ExtendedParsedFormat<absl::FormatConversionCharSet::s, + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::g>::New(format))); } TEST_F(ParsedFormatTest, RegressionMixPositional) { - EXPECT_FALSE((ExtendedParsedFormat< - FormatConversionCharSetInternal::d, - FormatConversionCharSetInternal::o>::New("%1$d %o"))); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::o>::New("%1$d %o"))); } using FormatWrapperTest = ::testing::Test; @@ -664,6 +706,38 @@ ABSL_NAMESPACE_END } // namespace absl +using FormatExtensionTest = ::testing::Test; + +struct Point { + friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString | + absl::FormatConversionCharSet::kIntegral> + AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec, + absl::FormatSink* s) { + if (spec.conversion_char() == absl::FormatConversionChar::s) { + s->Append(absl::StrCat("x=", p.x, " y=", p.y)); + } else { + s->Append(absl::StrCat(p.x, ",", p.y)); + } + return {true}; + } + + int x = 10; + int y = 20; +}; + +TEST_F(FormatExtensionTest, AbslFormatConvertExample) { + Point p; + EXPECT_EQ(absl::StrFormat("a %s z", p), "a x=10 y=20 z"); + EXPECT_EQ(absl::StrFormat("a %d z", p), "a 10,20 z"); + + // Typed formatting will fail to compile an invalid format. + // StrFormat("%f", p); // Does not compile. + std::string actual; + absl::UntypedFormatSpec f1("%f"); + // FormatUntyped will return false for bad character. + EXPECT_FALSE(absl::FormatUntyped(&actual, f1, {absl::FormatArg(p)})); +} + // Some codegen thunks that we can use to easily dump the generated assembly for // different StrFormat calls.
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h index a79cd4a..1ce17f3 100644 --- a/absl/strings/str_split.h +++ b/absl/strings/str_split.h
@@ -44,6 +44,7 @@ #include <vector> #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/strings/internal/str_split_internal.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h"
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 1f8a696..62fa8e9 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc
@@ -39,6 +39,7 @@ #include <thread> // NOLINT(build/c++11) #include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/internal/atomic_hook.h" @@ -85,28 +86,6 @@ kDeadlockDetectionDefault); ABSL_CONST_INIT std::atomic<bool> synch_check_invariants(false); -// ------------------------------------------ spinlock support - -// Make sure read-only globals used in the Mutex code are contained on the -// same cacheline and cacheline aligned to eliminate any false sharing with -// other globals from this and other modules. -static struct MutexGlobals { - MutexGlobals() { - // Find machine-specific data needed for Delay() and - // TryAcquireWithSpinning(). This runs in the global constructor - // sequence, and before that zeros are safe values. - num_cpus = absl::base_internal::NumCPUs(); - spinloop_iterations = num_cpus > 1 ? 1500 : 0; - } - int num_cpus; - int spinloop_iterations; - // Pad this struct to a full cacheline to prevent false sharing. - char padding[ABSL_CACHELINE_SIZE - 2 * sizeof(int)]; -} ABSL_CACHELINE_ALIGNED mutex_globals; -static_assert( - sizeof(MutexGlobals) == ABSL_CACHELINE_SIZE, - "MutexGlobals must occupy an entire cacheline to prevent false sharing"); - ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)> submit_profile_data; @@ -143,7 +122,22 @@ symbolizer.Store(fn); } -// spinlock delay on iteration c. Returns new c. +struct ABSL_CACHELINE_ALIGNED MutexGlobals { + absl::once_flag once; + int num_cpus = 0; + int spinloop_iterations = 0; +}; + +static const MutexGlobals& GetMutexGlobals() { + ABSL_CONST_INIT static MutexGlobals data; + absl::base_internal::LowLevelCallOnce(&data.once, [&]() { + data.num_cpus = absl::base_internal::NumCPUs(); + data.spinloop_iterations = data.num_cpus > 1 ? 1500 : 0; + }); + return data; +} + +// Spinlock delay on iteration c. Returns new c. namespace { enum DelayMode { AGGRESSIVE, GENTLE }; }; @@ -153,22 +147,25 @@ // gentle then spin only a few times before yielding. Aggressive spinning is // used to ensure that an Unlock() call, which must get the spin lock for // any thread to make progress gets it without undue delay. - int32_t limit = (mutex_globals.num_cpus > 1) ? - ((mode == AGGRESSIVE) ? 5000 : 250) : 0; + const int32_t limit = + GetMutexGlobals().num_cpus > 1 ? (mode == AGGRESSIVE ? 5000 : 250) : 0; if (c < limit) { - c++; // spin + // Spin. + c++; } else { ABSL_TSAN_MUTEX_PRE_DIVERT(nullptr, 0); - if (c == limit) { // yield once + if (c == limit) { + // Yield once. AbslInternalMutexYield(); c++; - } else { // then wait + } else { + // Then wait. absl::SleepFor(absl::Microseconds(10)); c = 0; } ABSL_TSAN_MUTEX_POST_DIVERT(nullptr, 0); } - return (c); + return c; } // --------------------------Generic atomic ops @@ -1437,7 +1434,7 @@ // Attempt to acquire *mu, and return whether successful. The implementation // may spin for a short while if the lock cannot be acquired immediately. static bool TryAcquireWithSpinning(std::atomic<intptr_t>* mu) { - int c = mutex_globals.spinloop_iterations; + int c = GetMutexGlobals().spinloop_iterations; do { // do/while somewhat faster on AMD intptr_t v = mu->load(std::memory_order_relaxed); if ((v & (kMuReader|kMuEvent)) != 0) {
diff --git a/absl/time/duration.cc b/absl/time/duration.cc index d0f1aad..952cc09 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc
@@ -69,6 +69,7 @@ #include "absl/base/casts.h" #include "absl/base/macros.h" #include "absl/numeric/int128.h" +#include "absl/strings/string_view.h" #include "absl/strings/strip.h" #include "absl/time/time.h" @@ -710,16 +711,17 @@ // fractional digits, because it is in the noise of what a Duration can // represent. struct DisplayUnit { - const char* abbr; + absl::string_view abbr; int prec; double pow10; }; -const DisplayUnit kDisplayNano = {"ns", 2, 1e2}; -const DisplayUnit kDisplayMicro = {"us", 5, 1e5}; -const DisplayUnit kDisplayMilli = {"ms", 8, 1e8}; -const DisplayUnit kDisplaySec = {"s", 11, 1e11}; -const DisplayUnit kDisplayMin = {"m", -1, 0.0}; // prec ignored -const DisplayUnit kDisplayHour = {"h", -1, 0.0}; // prec ignored +ABSL_CONST_INIT const DisplayUnit kDisplayNano = {"ns", 2, 1e2}; +ABSL_CONST_INIT const DisplayUnit kDisplayMicro = {"us", 5, 1e5}; +ABSL_CONST_INIT const DisplayUnit kDisplayMilli = {"ms", 8, 1e8}; +ABSL_CONST_INIT const DisplayUnit kDisplaySec = {"s", 11, 1e11}; +ABSL_CONST_INIT const DisplayUnit kDisplayMin = {"m", -1, 0.0}; // prec ignored +ABSL_CONST_INIT const DisplayUnit kDisplayHour = {"h", -1, + 0.0}; // prec ignored void AppendNumberUnit(std::string* out, int64_t n, DisplayUnit unit) { char buf[sizeof("2562047788015216")]; // hours in max duration @@ -727,7 +729,7 @@ char* bp = Format64(ep, 0, n); if (*bp != '0' || bp + 1 != ep) { out->append(bp, ep - bp); - out->append(unit.abbr); + out->append(unit.abbr.data(), unit.abbr.size()); } } @@ -750,7 +752,7 @@ while (ep[-1] == '0') --ep; out->append(bp, ep - bp); } - out->append(unit.abbr); + out->append(unit.abbr.data(), unit.abbr.size()); } }
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h index 4cde96f..d1b4222 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -106,54 +106,64 @@ CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { - y += (cd / 146097) * 400; + year_t ey = y % 400; + const year_t oey = ey; + ey += (cd / 146097) * 400; cd %= 146097; if (cd < 0) { - y -= 400; + ey -= 400; cd += 146097; } - y += (d / 146097) * 400; + ey += (d / 146097) * 400; d = d % 146097 + cd; if (d > 0) { if (d > 146097) { - y += 400; + ey += 400; d -= 146097; } } else { if (d > -365) { // We often hit the previous year when stepping a civil time backwards, // so special case it to avoid counting up by 100/4/1-year chunks. - y -= 1; - d += days_per_year(y, m); + ey -= 1; + d += days_per_year(ey, m); } else { - y -= 400; + ey -= 400; d += 146097; } } if (d > 365) { - for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) { + for (;;) { + int n = days_per_century(ey, m); + if (d <= n) break; d -= n; - y += 100; + ey += 100; } - for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) { + for (;;) { + int n = days_per_4years(ey, m); + if (d <= n) break; d -= n; - y += 4; + ey += 4; } - for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) { + for (;;) { + int n = days_per_year(ey, m); + if (d <= n) break; d -= n; - ++y; + ++ey; } } if (d > 28) { - for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) { + for (;;) { + int n = days_per_month(ey, m); + if (d <= n) break; d -= n; if (++m > 12) { - ++y; + ++ey; m = 1; } } } - return fields(y, m, static_cast<day_t>(d), hh, mm, ss); + return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss); } CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept {
diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc index be894d7..a5a7123 100644 --- a/absl/time/internal/cctz/src/civil_time_test.cc +++ b/absl/time/internal/cctz/src/civil_time_test.cc
@@ -235,6 +235,16 @@ } // NOTE: Run this with --copt=-ftrapv to detect overflow problems. +TEST(CivilTime, ConstructionWithHugeYear) { + constexpr civil_hour h(-9223372036854775807, 1, 1, -1); + static_assert(h.year() == -9223372036854775807 - 1, + "ConstructionWithHugeYear"); + static_assert(h.month() == 12, "ConstructionWithHugeYear"); + static_assert(h.day() == 31, "ConstructionWithHugeYear"); + static_assert(h.hour() == 23, "ConstructionWithHugeYear"); +} + +// NOTE: Run this with --copt=-ftrapv to detect overflow problems. TEST(CivilTime, DifferenceWithHugeYear) { { constexpr civil_day d1(9223372036854775807, 1, 1);
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h index 71bd3ad..d404e80 100644 --- a/absl/types/internal/variant.h +++ b/absl/types/internal/variant.h
@@ -292,7 +292,7 @@ template <class Op, std::size_t I> struct ReachableSwitchCase { static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) { - return absl::base_internal::Invoke(absl::forward<Op>(op), SizeT<I>()); + return absl::base_internal::invoke(absl::forward<Op>(op), SizeT<I>()); } }; @@ -424,7 +424,7 @@ return PickCase<Op, 32, EndIndex>::Run(absl::forward<Op>(op)); default: ABSL_ASSERT(i == variant_npos); - return absl::base_internal::Invoke(absl::forward<Op>(op), NPos()); + return absl::base_internal::invoke(absl::forward<Op>(op), NPos()); } } }; @@ -488,7 +488,7 @@ template <std::size_t I> VisitIndicesResultT<Op, decltype(EndIndices)...> operator()( SizeT<I> /*index*/) && { - return base_internal::Invoke( + return base_internal::invoke( absl::forward<Op>(op), SizeT<UnflattenIndex<I, N, (EndIndices + 1)...>::value - std::size_t{1}>()...); @@ -930,7 +930,7 @@ absl::result_of_t<Op(VariantAccessResult< Is, QualifiedVariants>...)>>::value, "All visitation overloads must have the same return type."); - return absl::base_internal::Invoke( + return absl::base_internal::invoke( absl::forward<Op>(op), VariantCoreAccess::Access<Is>( absl::forward<QualifiedVariants>(std::get<TupIs>(variant_tup)))...);
diff --git a/absl/types/span.h b/absl/types/span.h index 734db69..95fe792 100644 --- a/absl/types/span.h +++ b/absl/types/span.h
@@ -17,32 +17,30 @@ // span.h // ----------------------------------------------------------------------------- // -// This header file defines a `Span<T>` 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. +// This header file defines a `Span<T>` type for holding a reference to existing +// array data. The `Span` object, much like the `absl::string_view` object, +// does not own such data itself, and the data being referenced by the span must +// outlive the span itself. Unlike `view` type references, a span can hold a +// reference to mutable data (and can mutate it for underlying types of +// non-const T.) A span provides a lightweight way to pass a reference to such +// data. // // Additionally, this header file defines `MakeSpan()` and `MakeConstSpan()` // factory functions, for clearly creating spans of type `Span<T>` or read-only // `Span<const T>` 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` +// The C++20 draft standard includes a `std::span` type. As of June 2020, the +// differences between `absl::Span` and `std::span` are: +// * `absl::Span` has `operator==` (which is likely a design bug, +// per https://abseil.io/blog/20180531-regular-types) // * `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 +// * `absl::Span` has no `element_type` typedef // * A read-only `absl::Span<const T>` can be implicitly constructed from an // initializer list. // * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or @@ -77,9 +75,9 @@ // 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 +// A `Span` is an "array reference" type for holding a reference of contiguous +// array data; 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. @@ -97,7 +95,8 @@ // constructors. // // A `Span<T>` 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 +// of elements of type `T`, and unlike an `absl::string_view`, a span can hold a +// reference to mutable data. A user of `Span` must ensure that the data being // pointed to outlives the `Span` itself. // // You can construct a `Span<T>` in several ways: @@ -127,7 +126,7 @@ // 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 +// associated spans should not invoke operations that may reallocate memory // (such as resizing) or invalidate iterators into the container. // // One common use for a `Span` is when passing arguments to a routine that can
diff --git a/absl/utility/utility.h b/absl/utility/utility.h index e6647c7..bf92322 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h
@@ -236,10 +236,10 @@ // Helper method for expanding tuple into a called method. template <typename Functor, typename Tuple, std::size_t... Indexes> auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>) - -> decltype(absl::base_internal::Invoke( + -> decltype(absl::base_internal::invoke( absl::forward<Functor>(functor), std::get<Indexes>(absl::forward<Tuple>(t))...)) { - return absl::base_internal::Invoke( + return absl::base_internal::invoke( absl::forward<Functor>(functor), std::get<Indexes>(absl::forward<Tuple>(t))...); }