/* * Copyright (C) 2022 The Android Open Source Project * * 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. */ #pragma once #include #include #include #include #include /** * @file: Implement Contains(container, key) * * The function returns true if container has the key, or false. * * If the container has a find(key) method (e.g. set, unordered_set, std::map, * etc), the find method is used. Otherwise, the std::find function from * algorithm is used, which may result in a linear search. * * See go/cf-utils-contains for more details. */ namespace cuttlefish { namespace contains_internal_impl { /* * If Container does not have find key, will be a compiler error used * by SFINAE. If it does have one, this is equivalent to the "void" type. */ template using VoidTypeIfHasFind = decltype(void(std::declval().find(std::declval()))); /* * Here is how this works: * * Given that * HasFindImpl is used in the code * * 1. The input is effectively regarded as HasFindImpl. * The specialized version below isn't looked up yet; whether the specialized * version below is used or not, the compiler front-end needs all three * template parameters to match against either special or generic version. * When obtaining "all three," the front-end only looks up the base template * definition. The default type of the third template parameter is void, so * the given type is expanded/deduced to HasFindImpl. * * 2. Now, given HasFindImpl, the compiler front-end * tries matching against the specialized and generic/original versions. If * the input could matches both a generic and a specialized one, the compiler * chooses the specialized one. Thus, particularly, HasFindImpl * implementation's third parameter in the specialized version must be the * same as the default type of the third template parameter to the original/ * generic version, which is "void." */ template struct HasFindImpl : std::false_type {}; template struct HasFindImpl> : std::true_type {}; template using RemoveCvref = typename std::remove_cv_t>; template using IsSame = typename std::is_same, RemoveCvref>; template struct IsString : IsSame {}; template struct IsStringView : IsSame {}; } // namespace contains_internal_impl // TODO(kwstephenkim): Replace these when C++20 starts to be used. template ::value && (!contains_internal_impl::IsString::value && !contains_internal_impl::IsStringView::value), void>> constexpr bool Contains(Container&& container, U&& u) { // using O(1) or O(lgN) find() return container.find(std::forward(u)) != container.end(); } template < typename Container, typename U, std::enable_if_t::value, int> = 0> constexpr bool Contains(Container&& container, U&& u) { // falls back to a generic, likely linear search const auto itr = std::find(std::begin(container), std::end(container), std::forward(u)); return itr != std::end(container); } // std::string:: or std::string_view::find() returns index, not iterator template constexpr bool Contains(const std::string& s, T&& t) { return s.find(std::forward(t)) != std::string::npos; } template constexpr bool Contains(const std::string_view& s, T&& t) { return s.find(std::forward(t)) != std::string_view::npos; } } // namespace cuttlefish