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.

PiperOrigin-RevId: 687923300
  • Loading branch information
jwhpryor authored and copybara-github committed Oct 21, 2024
1 parent acce094 commit 71ae109
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 4 deletions.
23 changes: 19 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ...
17 changes: 17 additions & 0 deletions metaprogramming/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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.
################################################################################
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 71ae109

Please sign in to comment.