From cfc4a3584413d998105a0d7edab45d3308141ab2 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 --- metaprogramming/BUILD | 17 +++++++ metaprogramming/lambda_string_test.cc | 2 + metaprogramming/string_literal.h | 66 ++++++++++++++++++++++++++ metaprogramming/string_literal_test.cc | 56 ++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 metaprogramming/string_literal.h create mode 100644 metaprogramming/string_literal_test.cc 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/lambda_string_test.cc b/metaprogramming/lambda_string_test.cc index 9579bc2b..9301e28d 100644 --- a/metaprogramming/lambda_string_test.cc +++ b/metaprogramming/lambda_string_test.cc @@ -16,6 +16,8 @@ #include "lambda_string.h" +#include +#include #include #include 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