From 9fbfc244c47ca9cf6828c7367a06ee2d70e77f45 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. Some previously stripped tags have been restored on targets in order to correctly filter C++20 targets. PiperOrigin-RevId: 687923300 --- BUILD | 3 ++ javatests/com/jnibind/test/BUILD | 15 ++++++ metaprogramming/BUILD | 19 ++++++++ metaprogramming/string_literal.h | 66 ++++++++++++++++++++++++++ metaprogramming/string_literal_test.cc | 56 ++++++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 metaprogramming/string_literal.h create mode 100644 metaprogramming/string_literal_test.cc diff --git a/BUILD b/BUILD index daced343..7949e520 100644 --- a/BUILD +++ b/BUILD @@ -111,6 +111,7 @@ cc_library( name = "jni_test", testonly = 1, hdrs = ["jni_test.h"], + tags = ["nozapfhahn"], visibility = [":__subpackages__"], deps = [ ":jni_bind", @@ -124,6 +125,7 @@ cc_library( name = "mock_jni_env", testonly = 1, hdrs = ["mock_jni_env.h"], + tags = ["nozapfhahn"], visibility = [":__subpackages__"], deps = [ "//:jni_dep", @@ -135,6 +137,7 @@ cc_library( name = "mock_jvm", testonly = 1, hdrs = ["mock_jvm.h"], + tags = ["nozapfhahn"], visibility = [":__subpackages__"], deps = [ "//:jni_dep", diff --git a/javatests/com/jnibind/test/BUILD b/javatests/com/jnibind/test/BUILD index 98e0902e..8af8d7f6 100644 --- a/javatests/com/jnibind/test/BUILD +++ b/javatests/com/jnibind/test/BUILD @@ -32,6 +32,7 @@ java_test( srcs = ["ArrayTestFieldRank1.java"], data = [":libarray_test_field_rank_1_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], runtime_deps = [":array_test_helpers"], deps = [ ":object_test_helper", @@ -69,6 +70,7 @@ java_test( srcs = ["ArrayTestFieldRank2.java"], data = [":libarray_test_field_rank_2_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], runtime_deps = [ ":array_test_helpers", ], @@ -133,6 +135,7 @@ java_test( srcs = ["ArrayTestMethodRank1.java"], data = [":libarray_test_method_rank_1_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], runtime_deps = [ ":array_test_helpers", ], @@ -173,6 +176,7 @@ java_test( srcs = ["ArrayTestMethodRank2.java"], data = [":libarray_test_method_rank_2_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], runtime_deps = [ ":array_test_helpers", ], @@ -210,6 +214,7 @@ java_test( srcs = ["BuilderTest.java"], data = [":libbuilder_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ ":object_test_helper", "@maven//:junit_junit", @@ -261,6 +266,7 @@ java_test( ":libclass_loader_test_jni.so", ], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = ["@maven//:junit_junit"], ) @@ -290,6 +296,7 @@ java_test( srcs = ["ContextTest.java"], data = [":libcontext_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ ":object_test_helper", "@maven//:com_google_truth_truth", @@ -328,6 +335,7 @@ java_test( ], data = [":libfield_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ "@maven//:com_google_truth_truth", "@maven//:junit_junit", @@ -362,6 +370,7 @@ java_test( srcs = ["GlobalObjectTest.java"], data = [":libglobal_object_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ ":object_test_helper", "@maven//:com_google_truth_truth", @@ -377,6 +386,7 @@ cc_library( testonly = True, srcs = ["local_object_test_jni.cc"], defines = ["ENABLE_DEBUG_OUTPUT"], + tags = ["nosan"], deps = [ ":object_test_helper_jni", "//:jni_bind", @@ -397,6 +407,7 @@ java_test( srcs = ["LocalObjectTest.java"], data = [":liblocal_object_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ ":object_test_helper", "@maven//:com_google_truth_truth", @@ -435,6 +446,7 @@ java_test( ], data = [":libmethod_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ "@maven//:com_google_truth_truth", "@maven//:junit_junit", @@ -505,6 +517,7 @@ java_test( ], data = [":libstatic_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ ":object_test_helper", "@maven//:com_google_truth_truth", @@ -543,6 +556,7 @@ java_test( ], data = [":libstring_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], deps = [ "@maven//:com_google_truth_truth", "@maven//:junit_junit", @@ -577,6 +591,7 @@ java_test( srcs = ["ThreadTest.java"], data = [":libthread_test_jni.so"], jvm_flags = ["-Djava.library.path=./javatests/com/jnibind/test"], + tags = ["nosan"], runtime_deps = [":array_test_helpers"], deps = [ ":object_test_helper", diff --git a/metaprogramming/BUILD b/metaprogramming/BUILD index 06343b0a..52ec48db 100644 --- a/metaprogramming/BUILD +++ b/metaprogramming/BUILD @@ -1111,6 +1111,24 @@ cc_test( ], ) +################################################################################ +# String Literal. +################################################################################ +cc_library( + name = "string_literal", + hdrs = ["string_literal.h"], +) + +cc_test( + name = "string_literal_test", + srcs = ["string_literal_test.cc"], + tags = ["cpp20"], + deps = [ + ":string_literal", + "@googletest//:gtest_main", + ], +) + ################################################################################ # Type Index Mask. ################################################################################ @@ -1238,6 +1256,7 @@ cc_library( cc_test( name = "vals_equal_test", srcs = ["vals_equal_test.cc"], + tags = ["manual"], deps = [ ":vals", ":vals_equal", 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