Skip to content

Commit

Permalink
Experimenting with string literals for non-type template parameters.
Browse files Browse the repository at this point in the history
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 #4.

Some previously stripped tags have been restored on targets in order to correctly filter C++20 targets.

PiperOrigin-RevId: 687923300
  • Loading branch information
jwhpryor authored and copybara-github committed Oct 25, 2024
1 parent 5d69571 commit c94e4cc
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 0 deletions.
3 changes: 3 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ cc_library(
name = "jni_test",
testonly = 1,
hdrs = ["jni_test.h"],
tags = ["nozapfhahn"],
visibility = [":__subpackages__"],
deps = [
":jni_bind",
Expand All @@ -124,6 +125,7 @@ cc_library(
name = "mock_jni_env",
testonly = 1,
hdrs = ["mock_jni_env.h"],
tags = ["nozapfhahn"],
visibility = [":__subpackages__"],
deps = [
"//:jni_dep",
Expand All @@ -135,6 +137,7 @@ cc_library(
name = "mock_jvm",
testonly = 1,
hdrs = ["mock_jvm.h"],
tags = ["nozapfhahn"],
visibility = [":__subpackages__"],
deps = [
"//:jni_dep",
Expand Down
15 changes: 15 additions & 0 deletions javatests/com/jnibind/test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
],
Expand Down Expand Up @@ -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",
],
Expand Down Expand Up @@ -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",
],
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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"],
)

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
19 changes: 19 additions & 0 deletions metaprogramming/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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.
################################################################################
Expand Down Expand Up @@ -1238,6 +1256,7 @@ cc_library(
cc_test(
name = "vals_equal_test",
srcs = ["vals_equal_test.cc"],
tags = ["manual"],
deps = [
":vals",
":vals_equal",
Expand Down
66 changes: 66 additions & 0 deletions metaprogramming/string_literal.h
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <cstddef>
#include <string_view>

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 <size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }

constexpr StringLiteral<N> Self() { return *this; }

template <std::size_t U>
constexpr bool operator==(const StringLiteral<U>& rhs) const {
if constexpr (N != U) {
return false;
} else {
return std::string_view{value} == std::string_view{rhs.value};
}
}

template <std::size_t U>
constexpr bool operator!=(const StringLiteral<U>& rhs) const {
return !(*this == rhs);
}

char value[N];
};

template <size_t N>
StringLiteral(const char (&str)[N]) -> StringLiteral<N>;

#endif // __cplusplus >= 202002L
#endif // __cplusplus

} // namespace jni::metaprogramming

#endif // JNI_BIND_METAPROGRAMMING_STRING_LITERAL_H_
56 changes: 56 additions & 0 deletions metaprogramming/string_literal_test.cc
Original file line number Diff line number Diff line change
@@ -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 <cstddef>

using jni::metaprogramming::StringLiteral;

namespace {

template <size_t N>
constexpr StringLiteral<N> 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 <std::size_t N>
struct SampleContainer {
constexpr SampleContainer(const char (&val_)[N]) : val_(val_) {}

StringLiteral<N> val_;
};

template <std::size_t N>
SampleContainer(const char (&val_)[N]) -> SampleContainer<N>;

static_assert(SampleContainer("Testing").val_ == StringLiteral{"Testing"});
static_assert(SampleContainer("abcdefg").val_ != StringLiteral{"Testing"});
static_assert(SampleContainer("Foo").val_ != StringLiteral{"Testing"});

} // namespace

0 comments on commit c94e4cc

Please sign in to comment.