blob: 97361c12626e671363a5949fc4655defea40da09 [file] [log] [blame] [edit]
/*
* 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 <algorithm>
#include <iterator>
#include <string>
#include <string_view>
#include <type_traits>
/**
* @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 <typename Container, typename Key>
using VoidTypeIfHasFind =
decltype(void(std::declval<Container&>().find(std::declval<Key&>())));
/*
* Here is how this works:
*
* Given that
* HasFindImpl<Container, T> is used in the code
*
* 1. The input is effectively regarded as HasFindImpl<Container, T, void>.
* 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<Container, T, void>.
*
* 2. Now, given HasFindImpl<Container, T, void>, 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 <typename Container, typename T, typename = void>
struct HasFindImpl : std::false_type {};
template <typename Container, typename T>
struct HasFindImpl<Container, T, VoidTypeIfHasFind<Container, T>>
: std::true_type {};
template <typename T>
using RemoveCvref =
typename std::remove_cv_t<typename std::remove_reference_t<T>>;
template <typename T, typename U>
using IsSame = typename std::is_same<RemoveCvref<T>, RemoveCvref<U>>;
template <typename T>
struct IsString : IsSame<std::string, T> {};
template <typename T>
struct IsStringView : IsSame<std::string_view, T> {};
} // namespace contains_internal_impl
// TODO(kwstephenkim): Replace these when C++20 starts to be used.
template <typename Container, typename U,
typename = std::enable_if_t<
contains_internal_impl::HasFindImpl<Container, U>::value &&
(!contains_internal_impl::IsString<Container>::value &&
!contains_internal_impl::IsStringView<Container>::value),
void>>
constexpr bool Contains(Container&& container, U&& u) {
// using O(1) or O(lgN) find()
return container.find(std::forward<U>(u)) != container.end();
}
template <
typename Container, typename U,
std::enable_if_t<!contains_internal_impl::HasFindImpl<Container, U>::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>(u));
return itr != std::end(container);
}
// std::string:: or std::string_view::find() returns index, not iterator
template <typename T>
constexpr bool Contains(const std::string& s, T&& t) {
return s.find(std::forward<T>(t)) != std::string::npos;
}
template <typename T>
constexpr bool Contains(const std::string_view& s, T&& t) {
return s.find(std::forward<T>(t)) != std::string_view::npos;
}
} // namespace cuttlefish