about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--absl/strings/BUILD.bazel1
-rw-r--r--absl/strings/CMakeLists.txt1
-rw-r--r--absl/strings/internal/stl_type_traits.h213
-rw-r--r--absl/strings/internal/str_split_internal.h24
-rw-r--r--absl/strings/str_split_test.cc12
-rw-r--r--absl/strings/strip.h8
6 files changed, 241 insertions, 18 deletions
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index f6272586419b..13badb7b9c25 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -39,6 +39,7 @@ cc_library(
         "escaping.cc",
         "internal/memutil.cc",
         "internal/memutil.h",
+        "internal/stl_type_traits.h",
         "internal/str_join_internal.h",
         "internal/str_split_internal.h",
         "match.cc",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index f75b55a959ac..83cb934dba9d 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -35,6 +35,7 @@ list(APPEND STRINGS_INTERNAL_HEADERS
   "internal/memutil.h"
   "internal/ostringstream.h"
   "internal/resize_uninitialized.h"
+  "internal/stl_type_traits.h"
   "internal/str_join_internal.h"
   "internal/str_split_internal.h"
   "internal/utf8.h"
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h
new file mode 100644
index 000000000000..8c3d877e6356
--- /dev/null
+++ b/absl/strings/internal/stl_type_traits.h
@@ -0,0 +1,213 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Thie file provides the IsStrictlyBaseOfAndConvertibleToSTLContainer type
+// trait metafunction to assist in working with the _GLIBCXX_DEBUG debug
+// wrappers of STL containers.
+//
+// DO NOT INCLUDE THIS FILE DIRECTLY. Use this file by including
+// absl/strings/str_split.h.
+//
+// IWYU pragma: private, include "absl/strings/str_split.h"
+
+#ifndef ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
+#define ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
+
+#include <array>
+#include <bitset>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <map>
+#include <set>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+namespace strings_internal {
+
+template <typename C, template <typename...> class T>
+struct IsSpecializationImpl : std::false_type {};
+template <template <typename...> class T, typename... Args>
+struct IsSpecializationImpl<T<Args...>, T> : std::true_type {};
+template <typename C, template <typename...> class T>
+using IsSpecialization = IsSpecializationImpl<absl::decay_t<C>, T>;
+
+template <typename C>
+struct IsArrayImpl : std::false_type {};
+template <template <typename, size_t> class A, typename T, size_t N>
+struct IsArrayImpl<A<T, N>> : std::is_same<A<T, N>, std::array<T, N>> {};
+template <typename C>
+using IsArray = IsArrayImpl<absl::decay_t<C>>;
+
+template <typename C>
+struct IsBitsetImpl : std::false_type {};
+template <template <size_t> class B, size_t N>
+struct IsBitsetImpl<B<N>> : std::is_same<B<N>, std::bitset<N>> {};
+template <typename C>
+using IsBitset = IsBitsetImpl<absl::decay_t<C>>;
+
+template <typename C>
+struct IsSTLContainer
+    : absl::disjunction<
+          IsArray<C>, IsBitset<C>, IsSpecialization<C, std::deque>,
+          IsSpecialization<C, std::forward_list>,
+          IsSpecialization<C, std::list>, IsSpecialization<C, std::map>,
+          IsSpecialization<C, std::multimap>, IsSpecialization<C, std::set>,
+          IsSpecialization<C, std::multiset>,
+          IsSpecialization<C, std::unordered_map>,
+          IsSpecialization<C, std::unordered_multimap>,
+          IsSpecialization<C, std::unordered_set>,
+          IsSpecialization<C, std::unordered_multiset>,
+          IsSpecialization<C, std::vector>> {};
+
+template <typename C, template <typename...> class T, typename = void>
+struct IsBaseOfSpecializationImpl : std::false_type {};
+// IsBaseOfSpecializationImpl must have three partial specializations,
+// because we must only compare templates that take the same number of
+// arguments. Otherwise, for example, std::vector can be compared with std::map,
+// and fail to compile because of too few or too many template arguments.
+//
+// We must also SFINAE on the existence of an allocator_type. Otherwise, we may
+// try to compare, for example, a std::pair<std::string, std::string> with a
+// std::vector<std::string, std:std::string>. This would fail to compile, because
+// of expected properties of the type passed in as the allocator.
+template <template <typename, typename> class U,
+          template <typename, typename> class T, typename... Args>
+struct IsBaseOfSpecializationImpl<
+    U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>>
+    : std::is_base_of<U<Args...>, T<Args...>> {};
+template <template <typename, typename, typename> class U,
+          template <typename, typename, typename> class T, typename... Args>
+struct IsBaseOfSpecializationImpl<
+    U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>>
+    : std::is_base_of<U<Args...>, T<Args...>> {};
+template <template <typename, typename, typename, typename> class U,
+          template <typename, typename, typename, typename> class T,
+          typename... Args>
+struct IsBaseOfSpecializationImpl<
+    U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>>
+    : std::is_base_of<U<Args...>, T<Args...>> {};
+template <typename C, template <typename...> class T>
+using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<absl::decay_t<C>, T>;
+
+template <typename C>
+struct IsBaseOfArrayImpl : std::false_type {};
+template <template <typename, size_t> class A, typename T, size_t N>
+struct IsBaseOfArrayImpl<A<T, N>> : std::is_base_of<A<T, N>, std::array<T, N>> {
+};
+template <typename C>
+using IsBaseOfArray = IsBaseOfArrayImpl<absl::decay_t<C>>;
+
+template <typename C>
+struct IsBaseOfBitsetImpl : std::false_type {};
+template <template <size_t> class B, size_t N>
+struct IsBaseOfBitsetImpl<B<N>> : std::is_base_of<B<N>, std::bitset<N>> {};
+template <typename C>
+using IsBaseOfBitset = IsBaseOfBitsetImpl<absl::decay_t<C>>;
+
+template <typename C>
+struct IsBaseOfSTLContainer
+    : absl::disjunction<IsBaseOfArray<C>, IsBaseOfBitset<C>,
+                        IsBaseOfSpecialization<C, std::deque>,
+                        IsBaseOfSpecialization<C, std::forward_list>,
+                        IsBaseOfSpecialization<C, std::list>,
+                        IsBaseOfSpecialization<C, std::map>,
+                        IsBaseOfSpecialization<C, std::multimap>,
+                        IsBaseOfSpecialization<C, std::set>,
+                        IsBaseOfSpecialization<C, std::multiset>,
+                        IsBaseOfSpecialization<C, std::unordered_map>,
+                        IsBaseOfSpecialization<C, std::unordered_multimap>,
+                        IsBaseOfSpecialization<C, std::unordered_set>,
+                        IsBaseOfSpecialization<C, std::unordered_multiset>,
+                        IsBaseOfSpecialization<C, std::vector>> {};
+
+template <typename C, template <typename...> class T, typename = void>
+struct IsConvertibleToSpecializationImpl : std::false_type {};
+// IsConvertibleToSpecializationImpl must have three partial specializations,
+// because we must only compare templates that take the same number of
+// arguments. Otherwise, for example, std::vector can be compared with std::map,
+// and fail to compile because of too few or too many template arguments.
+//
+// We must also SFINAE on the existence of an allocator_type. Otherwise, we may
+// try to compare, for example, a std::pair<std::string, std::string> with a
+// std::vector<std::string, std:std::string>. This would fail to compile, because
+// of expected properties of the type passed in as the allocator.
+template <template <typename, typename> class U,
+          template <typename, typename> class T, typename... Args>
+struct IsConvertibleToSpecializationImpl<
+    U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>>
+    : std::is_convertible<U<Args...>, T<Args...>> {};
+template <template <typename, typename, typename> class U,
+          template <typename, typename, typename> class T, typename... Args>
+struct IsConvertibleToSpecializationImpl<
+    U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>>
+    : std::is_convertible<U<Args...>, T<Args...>> {};
+template <template <typename, typename, typename, typename> class U,
+          template <typename, typename, typename, typename> class T,
+          typename... Args>
+struct IsConvertibleToSpecializationImpl<
+    U<Args...>, T, absl::void_t<typename U<Args...>::allocator_type>>
+    : std::is_convertible<U<Args...>, T<Args...>> {};
+template <typename C, template <typename...> class T>
+using IsConvertibleToSpecialization =
+    IsConvertibleToSpecializationImpl<absl::decay_t<C>, T>;
+
+template <typename C>
+struct IsConvertibleToArrayImpl : std::false_type {};
+template <template <typename, size_t> class A, typename T, size_t N>
+struct IsConvertibleToArrayImpl<A<T, N>>
+    : std::is_convertible<A<T, N>, std::array<T, N>> {};
+template <typename C>
+using IsConvertibleToArray = IsConvertibleToArrayImpl<absl::decay_t<C>>;
+
+template <typename C>
+struct IsConvertibleToBitsetImpl : std::false_type {};
+template <template <size_t> class B, size_t N>
+struct IsConvertibleToBitsetImpl<B<N>>
+    : std::is_convertible<B<N>, std::bitset<N>> {};
+template <typename C>
+using IsConvertibleToBitset = IsConvertibleToBitsetImpl<absl::decay_t<C>>;
+
+template <typename C>
+struct IsConvertibleToSTLContainer
+    : absl::disjunction<
+          IsConvertibleToArray<C>, IsConvertibleToBitset<C>,
+          IsConvertibleToSpecialization<C, std::deque>,
+          IsConvertibleToSpecialization<C, std::forward_list>,
+          IsConvertibleToSpecialization<C, std::list>,
+          IsConvertibleToSpecialization<C, std::map>,
+          IsConvertibleToSpecialization<C, std::multimap>,
+          IsConvertibleToSpecialization<C, std::set>,
+          IsConvertibleToSpecialization<C, std::multiset>,
+          IsConvertibleToSpecialization<C, std::unordered_map>,
+          IsConvertibleToSpecialization<C, std::unordered_multimap>,
+          IsConvertibleToSpecialization<C, std::unordered_set>,
+          IsConvertibleToSpecialization<C, std::unordered_multiset>,
+          IsConvertibleToSpecialization<C, std::vector>> {};
+
+template <typename C>
+struct IsStrictlyBaseOfAndConvertibleToSTLContainer
+    : absl::conjunction<absl::negation<IsSTLContainer<C>>,
+                        IsBaseOfSTLContainer<C>,
+                        IsConvertibleToSTLContainer<C>> {};
+
+}  // namespace strings_internal
+}  // namespace absl
+#endif  // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index dc31a8ef9090..a1b10f3addcf 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -29,10 +29,6 @@
 #ifndef ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
 #define ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
 
-#ifdef _GLIBCXX_DEBUG
-#include <glibcxx_debug_traits.h>
-#endif  // _GLIBCXX_DEBUG
-
 #include <array>
 #include <initializer_list>
 #include <iterator>
@@ -46,15 +42,13 @@
 #include "absl/meta/type_traits.h"
 #include "absl/strings/string_view.h"
 
-namespace absl {
-namespace strings_internal {
-
 #ifdef _GLIBCXX_DEBUG
-using ::glibcxx_debug_traits::IsStrictlyDebugWrapperBase;
-#else  // _GLIBCXX_DEBUG
-template <typename T> struct IsStrictlyDebugWrapperBase : std::false_type {};
+#include "absl/strings/internal/stl_type_traits.h"
 #endif  // _GLIBCXX_DEBUG
 
+namespace absl {
+namespace strings_internal {
+
 // This class is implicitly constructible from everything that absl::string_view
 // is implicitly constructible from. If it's constructed from a temporary
 // std::string, the data is moved into a data member so its lifetime matches that of
@@ -237,10 +231,12 @@ struct IsInitializerList
 template <typename C>
 struct SplitterIsConvertibleTo
     : std::enable_if<
-          !IsStrictlyDebugWrapperBase<C>::value &&
-          !IsInitializerList<C>::value &&
-          HasValueType<C>::value &&
-          HasConstIterator<C>::value> {};
+#ifdef _GLIBCXX_DEBUG
+          !IsStrictlyBaseOfAndConvertibleToSTLContainer<C>::value &&
+#endif  // _GLIBCXX_DEBUG
+          !IsInitializerList<C>::value && HasValueType<C>::value &&
+          HasConstIterator<C>::value> {
+};
 
 // This class implements the range that is returned by absl::StrSplit(). This
 // class has templated conversion operators that allow it to be implicitly
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index 9c79d7dcd0bf..500f3cbc3789 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -166,6 +166,18 @@ TEST(Split, APIExamples) {
   }
 
   {
+    // Different forms of initialization / conversion.
+    std::vector<std::string> v1 = absl::StrSplit("a,b,c", ',');
+    EXPECT_THAT(v1, ElementsAre("a", "b", "c"));
+    std::vector<std::string> v2(absl::StrSplit("a,b,c", ','));
+    EXPECT_THAT(v2, ElementsAre("a", "b", "c"));
+    auto v3 = std::vector<std::string>(absl::StrSplit("a,b,c", ','));
+    EXPECT_THAT(v3, ElementsAre("a", "b", "c"));
+    v3 = absl::StrSplit("a,b,c", ',');
+    EXPECT_THAT(v3, ElementsAre("a", "b", "c"));
+  }
+
+  {
     // Results stored in a std::map.
     std::map<std::string, std::string> m = absl::StrSplit("a,1,b,2,a,3", ',');
     EXPECT_EQ(2, m.size());
diff --git a/absl/strings/strip.h b/absl/strings/strip.h
index 370f9e88f0d6..2f8d21f7deb9 100644
--- a/absl/strings/strip.h
+++ b/absl/strings/strip.h
@@ -67,8 +67,8 @@ inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
 // Returns a view into the input std::string 'str' with the given 'prefix' removed,
 // but leaving the original std::string intact. If the prefix does not match at the
 // start of the std::string, returns the original std::string instead.
-inline absl::string_view StripPrefix(absl::string_view str,
-                                     absl::string_view prefix) {
+ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
+    absl::string_view str, absl::string_view prefix) {
   if (absl::StartsWith(str, prefix)) str.remove_prefix(prefix.size());
   return str;
 }
@@ -78,8 +78,8 @@ inline absl::string_view StripPrefix(absl::string_view str,
 // Returns a view into the input std::string 'str' with the given 'suffix' removed,
 // but leaving the original std::string intact. If the suffix does not match at the
 // end of the std::string, returns the original std::string instead.
-inline absl::string_view StripSuffix(absl::string_view str,
-                                     absl::string_view suffix) {
+ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix(
+    absl::string_view str, absl::string_view suffix) {
   if (absl::EndsWith(str, suffix)) str.remove_suffix(suffix.size());
   return str;
 }