diff options
-rw-r--r-- | absl/strings/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/strings/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/strings/internal/stl_type_traits.h | 213 | ||||
-rw-r--r-- | absl/strings/internal/str_split_internal.h | 24 | ||||
-rw-r--r-- | absl/strings/str_split_test.cc | 12 | ||||
-rw-r--r-- | absl/strings/strip.h | 8 |
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; } |