summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Evan Brown <ezb@google.com>2023-04-28 13:15:48 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2023-04-28 13:16:48 -0700
commit56c5fc620161e12844b9b92d06f8954b29f40bf4 (patch)
treee27c5aea3eead747de49e67f04a252cc614d6665
parent65109ecdf01a829bdb5e428174b3abb181e75826 (diff)
Introduce IfConstexpr(Else) utilities for use internally in abseil library implementation.
These functions are meant to allow for avoiding complex SFINAE pre-C++17. PiperOrigin-RevId: 527968428 Change-Id: I6524206c9dc50663d7c38abf6d0e8080ca93fc30
-rw-r--r--CMake/AbseilDll.cmake1
-rw-r--r--absl/utility/BUILD.bazel23
-rw-r--r--absl/utility/CMakeLists.txt24
-rw-r--r--absl/utility/internal/if_constexpr.h70
-rw-r--r--absl/utility/internal/if_constexpr_test.cc79
5 files changed, 197 insertions, 0 deletions
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index 2a331435..32b51411 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -430,6 +430,7 @@ set(ABSL_INTERNAL_DLL_FILES
"types/span.h"
"types/internal/span.h"
"types/variant.h"
+ "utility/internal/if_constexpr.h"
"utility/utility.h"
"debugging/leak_check.cc"
)
diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel
index ca4bc0a6..061f4c5b 100644
--- a/absl/utility/BUILD.bazel
+++ b/absl/utility/BUILD.bazel
@@ -52,3 +52,26 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_library(
+ name = "if_constexpr",
+ hdrs = [
+ "internal/if_constexpr.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ ],
+)
+
+cc_test(
+ name = "if_constexpr_test",
+ srcs = ["internal/if_constexpr_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":if_constexpr",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt
index 865b758f..27ee0de5 100644
--- a/absl/utility/CMakeLists.txt
+++ b/absl/utility/CMakeLists.txt
@@ -42,3 +42,27 @@ absl_cc_test(
absl::strings
GTest::gmock_main
)
+
+absl_cc_library(
+ NAME
+ if_constexpr
+ HDRS
+ "internal/if_constexpr.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ if_constexpr_test
+ SRCS
+ "internal/if_constexpr_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::if_constexpr
+ GTest::gmock_main
+)
diff --git a/absl/utility/internal/if_constexpr.h b/absl/utility/internal/if_constexpr.h
new file mode 100644
index 00000000..7a26311d
--- /dev/null
+++ b/absl/utility/internal/if_constexpr.h
@@ -0,0 +1,70 @@
+// Copyright 2023 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.
+
+// The IfConstexpr and IfConstexprElse utilities in this file are meant to be
+// used to emulate `if constexpr` in pre-C++17 mode in library implementation.
+// The motivation is to allow for avoiding complex SFINAE.
+//
+// The functions passed in must depend on the type(s) of the object(s) that
+// require SFINAE. For example:
+// template<typename T>
+// int MaybeFoo(T& t) {
+// if constexpr (HasFoo<T>::value) return t.foo();
+// return 0;
+// }
+//
+// can be written in pre-C++17 as:
+//
+// template<typename T>
+// int MaybeFoo(T& t) {
+// int i = 0;
+// absl::utility_internal::IfConstexpr<HasFoo<T>::value>(
+// [&](const auto& fooer) { i = fooer.foo(); }, t);
+// return i;
+// }
+
+#ifndef ABSL_UTILITY_INTERNAL_IF_CONSTEXPR_H_
+#define ABSL_UTILITY_INTERNAL_IF_CONSTEXPR_H_
+
+#include <tuple>
+#include <utility>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace utility_internal {
+
+template <bool condition, typename TrueFunc, typename FalseFunc,
+ typename... Args>
+auto IfConstexprElse(TrueFunc&& true_func, FalseFunc&& false_func,
+ Args&&... args) {
+ return std::get<condition>(std::forward_as_tuple(
+ std::forward<FalseFunc>(false_func), std::forward<TrueFunc>(true_func)))(
+ std::forward<Args>(args)...);
+}
+
+template <bool condition, typename Func, typename... Args>
+void IfConstexpr(Func&& func, Args&&... args) {
+ IfConstexprElse<condition>(std::forward<Func>(func), [](auto&&...){},
+ std::forward<Args>(args)...);
+}
+
+} // namespace utility_internal
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_UTILITY_INTERNAL_IF_CONSTEXPR_H_
diff --git a/absl/utility/internal/if_constexpr_test.cc b/absl/utility/internal/if_constexpr_test.cc
new file mode 100644
index 00000000..d1ee7236
--- /dev/null
+++ b/absl/utility/internal/if_constexpr_test.cc
@@ -0,0 +1,79 @@
+// Copyright 2023 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/utility/internal/if_constexpr.h"
+
+#include <utility>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+struct Empty {};
+struct HasFoo {
+ int foo() const { return 1; }
+};
+
+TEST(IfConstexpr, Basic) {
+ int i = 0;
+ absl::utility_internal::IfConstexpr<false>(
+ [&](const auto& t) { i = t.foo(); }, Empty{});
+ EXPECT_EQ(i, 0);
+
+ absl::utility_internal::IfConstexpr<false>(
+ [&](const auto& t) { i = t.foo(); }, HasFoo{});
+ EXPECT_EQ(i, 0);
+
+ absl::utility_internal::IfConstexpr<true>(
+ [&](const auto& t) { i = t.foo(); }, HasFoo{});
+ EXPECT_EQ(i, 1);
+}
+
+TEST(IfConstexprElse, Basic) {
+ EXPECT_EQ(absl::utility_internal::IfConstexprElse<false>(
+ [&](const auto& t) { return t.foo(); }, [&](const auto&) { return 2; },
+ Empty{}), 2);
+
+ EXPECT_EQ(absl::utility_internal::IfConstexprElse<false>(
+ [&](const auto& t) { return t.foo(); }, [&](const auto&) { return 2; },
+ HasFoo{}), 2);
+
+ EXPECT_EQ(absl::utility_internal::IfConstexprElse<true>(
+ [&](const auto& t) { return t.foo(); }, [&](const auto&) { return 2; },
+ HasFoo{}), 1);
+}
+
+struct HasFooRValue {
+ int foo() && { return 1; }
+};
+struct RValueFunc {
+ void operator()(HasFooRValue&& t) && { *i = std::move(t).foo(); }
+
+ int* i = nullptr;
+};
+
+TEST(IfConstexpr, RValues) {
+ int i = 0;
+ RValueFunc func = {&i};
+ absl::utility_internal::IfConstexpr<false>(
+ std::move(func), HasFooRValue{});
+ EXPECT_EQ(i, 0);
+
+ func = RValueFunc{&i};
+ absl::utility_internal::IfConstexpr<true>(
+ std::move(func), HasFooRValue{});
+ EXPECT_EQ(i, 1);
+}
+
+} // namespace