`absl/meta`: Add C++17 port of C++20 `requires` expression for internal use PiperOrigin-RevId: 846259270 Change-Id: I0126a78949b15fbbafca7c8dba3e3b60df30914b
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index cc9f792..b419f1b 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -213,6 +213,7 @@ "log/vlog_is_on.h" "memory/memory.h" "meta/type_traits.h" + "meta/internal/requires.h" "numeric/bits.h" "numeric/int128.cc" "numeric/int128.h"
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel index f6efa42..6036895 100644 --- a/absl/meta/BUILD.bazel +++ b/absl/meta/BUILD.bazel
@@ -35,6 +35,29 @@ licenses(["notice"]) cc_library( + name = "requires", + hdrs = ["internal/requires.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl:__subpackages__"], + deps = [ + "//absl/base:config", + ], +) + +cc_test( + name = "requires_test", + srcs = ["internal/requires_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":requires", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( name = "type_traits", hdrs = ["type_traits.h"], copts = ABSL_DEFAULT_COPTS,
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt index d509114..20650cc 100644 --- a/absl/meta/CMakeLists.txt +++ b/absl/meta/CMakeLists.txt
@@ -16,6 +16,29 @@ absl_cc_library( NAME + requires_internal + HDRS + "internal/requires.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config +) + +absl_cc_test( + NAME + requires_test + SRCS + "internal/requires_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::requires_internal + GTest::gmock_main +) + +absl_cc_library( + NAME type_traits HDRS "type_traits.h"
diff --git a/absl/meta/internal/requires.h b/absl/meta/internal/requires.h new file mode 100644 index 0000000..2166e99 --- /dev/null +++ b/absl/meta/internal/requires.h
@@ -0,0 +1,67 @@ +// 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 +// +// 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_META_INTERNAL_REQUIRES_H_ +#define ABSL_META_INTERNAL_REQUIRES_H_ + +#include <type_traits> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace meta_internal { + +// C++17 port of the C++20 `requires` expressions. +// It allows easy inline test of properties of types in template code. +// https://en.cppreference.com/w/cpp/language/constraints#Requires_expressions +// +// Example usage: +// +// if constexpr (Requires<T>([](auto&& x) -> decltype(x.foo()) {})) { +// // T has foo() +// return t.foo(); +// } else if constexpr (Requires<T>([](auto&& x) -> decltype(Bar(x)) {})) { +// // Can call Bar with T +// return Bar(t); +// } else if constexpr (Requires<T, U>( +// // Can test expression with multiple inputs +// [](auto&& x, auto&& y) -> decltype(x + y) {})) { +// return t + t2; +// } +// +// The `Requires` function takes a list of types and a generic lambda where all +// arguments are of type `auto&&`. The lambda is never actually invoked and the +// body must be empty. +// When used this way, `Requires` returns whether the expression inside +// `decltype` is well-formed, when the lambda parameters have the types that +// are specified by the corresponding template arguments. +// +// NOTE: C++17 does not allow lambdas in template parameters, which means that +// code like the following is _not_ valid in C++17: +// +// template <typename T, +// typename = std::enable_if_t<gtl::Requires<T>( +// [] (auto&& v) -> decltype(<expr>) {})>> +// +template <typename... T, typename F> +constexpr bool Requires(F) { + return std::is_invocable_v<F, T...>; +} + +} // namespace meta_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_META_INTERNAL_REQUIRES_H_
diff --git a/absl/meta/internal/requires_test.cc b/absl/meta/internal/requires_test.cc new file mode 100644 index 0000000..046d8f3 --- /dev/null +++ b/absl/meta/internal/requires_test.cc
@@ -0,0 +1,66 @@ +// 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 +// +// 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/meta/internal/requires.h" + +#include <string> +#include <vector> + +#include "gtest/gtest.h" + +namespace { + +TEST(RequiresTest, SimpleLambdasWork) { + static_assert(absl::meta_internal::Requires([] {})); + static_assert(absl::meta_internal::Requires<int>([](auto&&) {})); + static_assert( + absl::meta_internal::Requires<int, char>([](auto&&, auto&&) {})); +} + +template <typename T> +inline constexpr bool has_cstr = + absl::meta_internal::Requires<T>([](auto&& x) -> decltype(x.c_str()) {}); + +template <typename T, typename U> +inline constexpr bool have_plus = absl::meta_internal::Requires<T, U>( + [](auto&& x, auto&& y) -> decltype(x + y) {}); + +TEST(RequiresTest, CanTestProperties) { + static_assert(has_cstr<std::string>); + static_assert(!has_cstr<std::vector<int>>); + + static_assert(have_plus<int, double>); + static_assert(have_plus<std::string, std::string>); + static_assert(!have_plus<std::string, double>); +} + +TEST(RequiresTest, WorksWithUnmovableTypes) { + struct S { + S(const S&) = delete; + int foo() { return 0; } + }; + static_assert( + absl::meta_internal::Requires<S>([](auto&& x) -> decltype(x.foo()) {})); + static_assert( + !absl::meta_internal::Requires<S>([](auto&& x) -> decltype(x.bar()) {})); +} + +TEST(RequiresTest, WorksWithArrays) { + static_assert( + absl::meta_internal::Requires<int[2]>([](auto&& x) -> decltype(x[1]) {})); + static_assert( + !absl::meta_internal::Requires<int[2]>([](auto&& x) -> decltype(-x) {})); +} + +} // namespace