From 3bc890a78514bb36800ee5a4816565e5aaa0748f Mon Sep 17 00:00:00 2001 From: Jamieson Pryor Date: Sun, 20 Oct 2024 15:50:37 -0700 Subject: [PATCH] Experimenting with string literals for non-type template parameters. C++20 offers string literals as non-type template parameters and this would likely allow us to move away from using a clang extension which would allow for gcc support. This is just experimentation for now. See https://github.com/google/jni-bind/issues/4. PiperOrigin-RevId: 687923300 --- .github/workflows/ci.yml | 23 +++++++-- metaprogramming/BUILD | 17 +++++++ metaprogramming/string_literal.h | 66 ++++++++++++++++++++++++++ metaprogramming/string_literal_test.cc | 56 ++++++++++++++++++++++ 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 metaprogramming/string_literal.h create mode 100644 metaprogramming/string_literal_test.cc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f31eaff..276cda92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,17 +11,32 @@ permissions: jobs: linux_test: - runs-on: ubuntu-latest + runs-on: ubuntu-latest-cpp17 steps: - uses: actions/checkout@v4 - name: test - run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors ... + run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors 'kind("...", //...) except attr("tags", "cpp20", "//...")' + + linux_test: + runs-on: ubuntu-latest-cpp20 + steps: + - uses: actions/checkout@v4 + - name: test + run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++20' --repo_env=CC=clang --test_output=errors ... + # NOTE: Mac is only running a small subset of targets due to an unresolved # issue in Bazel. When this is fixed in the future I will expand testing. macos_test: - runs-on: macos-latest + runs-on: macos-latest-cpp17 + steps: + - uses: actions/checkout@v4 + - name: test + run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors 'kind("...", //...) except attr("tags", "cpp20", "//metaprogramming/...")' + + macos_test: + runs-on: macos-latest-cpp20 steps: - uses: actions/checkout@v4 - name: test - run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors metaprogramming/... + run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++20' --repo_env=CC=clang --test_output=errors ... diff --git a/metaprogramming/BUILD b/metaprogramming/BUILD index 06343b0a..da03a3bc 100644 --- a/metaprogramming/BUILD +++ b/metaprogramming/BUILD @@ -1111,6 +1111,23 @@ cc_test( ], ) +################################################################################ +# String Literal. +################################################################################ +cc_library( + name = "string_literal", + hdrs = ["string_literal.h"], +) + +cc_test( + name = "string_literal_test", + srcs = ["string_literal_test.cc"], + deps = [ + ":string_literal", + "@googletest//:gtest_main", + ], +) + ################################################################################ # Type Index Mask. ################################################################################ diff --git a/metaprogramming/string_literal.h b/metaprogramming/string_literal.h new file mode 100644 index 00000000..e93a4b85 --- /dev/null +++ b/metaprogramming/string_literal.h @@ -0,0 +1,66 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef JNI_BIND_METAPROGRAMMING_STRING_LITERAL_H_ +#define JNI_BIND_METAPROGRAMMING_STRING_LITERAL_H_ + +#ifdef __cplusplus +#if __cplusplus >= 202002L + +#include +#include +#include + +namespace jni::metaprogramming { + +// Inspired by Kevin Hartman's StringLiteral implementation. +// https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/ +// +// This class is not currently being used, but is being used to prototype +// changes that will be needed for consumers of `InvocableMap`. This class is +// not included by default for build because it requires C++20. +template +struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } + + constexpr StringLiteral Self() { return *this; } + + template + constexpr bool operator==(const StringLiteral& rhs) const { + if constexpr (N != U) { + return false; + } else { + return std::string_view{value} == std::string_view{rhs.value}; + } + } + + template + constexpr bool operator!=(const StringLiteral& rhs) const { + return !(*this == rhs); + } + + char value[N]; +}; + +template +StringLiteral(const char (&str)[N]) -> StringLiteral; + +#endif // __cplusplus >= 202002L +#endif // __cplusplus + +} // namespace jni::metaprogramming + +#endif // JNI_BIND_METAPROGRAMMING_STRING_LITERAL_H_ diff --git a/metaprogramming/string_literal_test.cc b/metaprogramming/string_literal_test.cc new file mode 100644 index 00000000..485bfc70 --- /dev/null +++ b/metaprogramming/string_literal_test.cc @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#include "string_literal.h" + +#include + +using jni::metaprogramming::StringLiteral; + +namespace { + +template +constexpr StringLiteral Foo(const char (&str)[N]) { + StringLiteral b{str}; + return b; +} + +// String literals can now peer through callsites. This was previously not +// possible in C++17 and should hopefully simplify method selection. +static_assert(Foo("Testing") == StringLiteral{"Testing"}); +static_assert(Foo("abcdefg") != StringLiteral{"Testing"}); +static_assert(Foo("Testing") != StringLiteral{"abcdefg"}); +static_assert(Foo("Bar") != StringLiteral{"Testing"}); +static_assert(Foo("Testing") != StringLiteral{"Bar"}); + +// This represents a container class. Any classes currently constructed with +// const char* will need to migrate to this new form which explicitly captures +// length. +template +struct SampleContainer { + constexpr SampleContainer(const char (&val_)[N]) : val_(val_) {} + + StringLiteral val_; +}; + +template +SampleContainer(const char (&val_)[N]) -> SampleContainer; + +static_assert(SampleContainer("Testing").val_ == StringLiteral{"Testing"}); +static_assert(SampleContainer("abcdefg").val_ != StringLiteral{"Testing"}); +static_assert(SampleContainer("Foo").val_ != StringLiteral{"Testing"}); + +} // namespace