Skip to content

Add string_view API for c++17 #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions book/src/build/cargo.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,49 @@ manifest key][links]* in the Cargo reference.

[links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key

## Features
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if that's the place where you'd want to document features if at all. Just a placeholder.

Nicer to look at page: https://github.com/roligugus/cxx/blob/string_view/book/src/build/cargo.md#features


CXX provides features to enable additional functionality such as compiling
the CXX lib with a different standard.

Features are enabled on the CXX crate for Cargo-based setups, and with compiler
options for non-Cargo setups.

The following features are available, see `[features]` section in [Cargo.toml](https://github.com/dtolnay/cxx/blob/master/Cargo.toml) for a full list:
<table>
<tr><th>feature</th><th>compiler option</th><th>description</th></tr>
<tr><td>c++14</td><td>-std=c++14 (gcc, clang), /std:c++14 (msvc)</td><td>Compile CXX lib with c++14</td></tr>
<tr><td>c++17</td><td>-std=c++17 (gcc, clang), /std:c++17 (msvc)</td><td>Compile CXX lib with c++17</td></tr>
</table>

## Additional C++ standard features
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential documentation for enabling additional C++ APIs.

Nicer to look at page:
https://github.com/roligugus/cxx/blob/string_view/book/src/build/cargo.md#additional-c-standard-features

Some addition APIs are available if compiling your code with a newer C++ standard.

Please note, that these additional APIs are automatically enabled in the `cxx.h`
header if your code is compiled with the required minimal C++ standard. They do
not depend on building the actual CXX lib against the newer standard at this time.

<table>
<tr><th>Minimal C++ standard</th><th>Description</th></tr>
<tr><td>c++17</td><td>Enables additional `string_view` APIs for `rust::str` and `rust::String`</td></tr>
</table>

Example to e.g. enable the `string_view` APIs for your code, build at least with C++17:
```rust,noplayground
// build.rs

fn main() {
cxx_build::bridge("src/main.rs") // returns a cc::Build
.file("src/demo.cc")
.flag_if_supported("-std=c++17")
.compile("cxxbridge-demo");

println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/demo.cc");
println!("cargo:rerun-if-changed=include/demo.h");
}
```

<br><br><br>

# Advanced features
Expand Down
55 changes: 55 additions & 0 deletions include/cxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,45 @@
#include <basetsd.h>
#endif

#ifdef CXXBRIDGE_HAS_STRING_VIEW
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using CXXBRIDGE_.. naming to align more with other defines. WIP used CXX_... naming

Bikeshedding time?

#error "string_view gets auto-detected"
#endif

// clang-format off
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabling clang-format as settings will flatten everything which makes this hard to follow.

Using normal feature detection for compilers supporting it. Feature detection was also noted in the WIP PR.
Tested paths on godbolt.org as I don't have access to all the compiler versions.

Idea is that if string_view is supported, we should support it.

// detect if string_view supported (C++17 required)
#ifdef __has_include
#if __has_include(<version>)
// gcc >= 9, clang >= 9, msvc >= 19.22
#include <version>
#if __cpp_lib_string_view >= 201603L
#define CXXBRIDGE_HAS_STRING_VIEW
#endif
#else
// no <version> include
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201603L
// gcc: 8, clang: 5 - 8, msvc: 19.15 - 19.21
#define CXXBRIDGE_HAS_STRING_VIEW
#else
// only include if compiled with c++17
#if (__GNUC__ >= 7 && __cplusplus >= 201703L) || (_MSVC_LANG >= 201703L)
// gcc: 7, msvc: 19.14
#define CXXBRIDGE_HAS_STRING_VIEW
#endif
#endif
#endif
#else
// no __has_include
#if _MSC_VER >= 1910L && _MSVC_LANG > 201402L
// msvc: 19.10 requires c++latest (!)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Older msvc compilers that supported string_view. Not sure what compiler versions you want to support, but I've included the ones I could get my hands on through godbolt.

There could be a discussion if it would make sense to support "c++latest" in cxx as a feature (for msvc).

#define CPPBRIDGE_HAS_STRING_VIEW
#endif
#endif
// clang-format on

#ifdef CXXBRIDGE_HAS_STRING_VIEW
#include <string_view>
#endif

namespace rust {
inline namespace cxxbridge1 {

Expand All @@ -34,6 +73,9 @@ class String final {
String(String &&) noexcept;
~String() noexcept;

#ifdef CXXBRIDGE_HAS_STRING_VIEW
String(std::string_view sv) : String(sv.data(), sv.length()) {}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add "always inline", e.g. __attribute__((always_inline)) (gcc/clang) and __forceinline (msvc), to make sure it gets inlined even if e.g. people disable inlining, etc.

#endif
String(const std::string &);
String(const char *);
String(const char *, size_t);
Expand All @@ -42,6 +84,11 @@ class String final {
String &operator=(String &&) noexcept;

explicit operator std::string() const;
#ifdef CXXBRIDGE_HAS_STRING_VIEW
explicit operator std::string_view() const noexcept {
return {this->data(), this->size()};
}
#endif

// Note: no null terminator.
const char *data() const noexcept;
Expand Down Expand Up @@ -71,13 +118,21 @@ class String final {
class Str final {
public:
Str() noexcept;
#ifdef CXXBRIDGE_HAS_STRING_VIEW
Str(std::string_view sv) : Str(sv.data(), sv.length()) {}
#endif
Str(const std::string &);
Str(const char *);
Str(const char *, size_t);

Str &operator=(const Str &) noexcept = default;

explicit operator std::string() const;
#ifdef CXXBRIDGE_HAS_STRING_VIEW
explicit operator std::string_view() const noexcept {
return {this->data(), this->size()};
}
#endif

// Note: no null terminator.
const char *data() const noexcept;
Expand Down
10 changes: 10 additions & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,10 @@ pub mod ffi {
fn r_take_ref_r(r: &R);
fn r_take_ref_c(c: &C);
fn r_take_str(s: &str);
fn r_take_empty_str(s: &str);
fn r_take_slice_char(s: &[c_char]);
fn r_take_rust_string(s: String);
fn r_take_empty_rust_string(s: String);
fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
fn r_take_ref_vector(v: &CxxVector<u8>);
fn r_take_ref_empty_vector(v: &CxxVector<u64>);
Expand Down Expand Up @@ -470,10 +472,18 @@ fn r_take_str(s: &str) {
assert_eq!(s, "2020");
}

fn r_take_empty_str(s: &str) {
assert_eq!(s.len(), 0);
}

fn r_take_rust_string(s: String) {
assert_eq!(s, "2020");
}

fn r_take_empty_rust_string(s: String) {
assert_eq!(s.len(), 0);
}

fn r_take_slice_char(s: &[c_char]) {
assert_eq!(s.len(), 5);
let s = cast::c_char_to_unsigned(s);
Expand Down
16 changes: 16 additions & 0 deletions tests/ffi/tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,24 @@ extern "C" const char *cxx_run_test() noexcept {
r_take_unique_ptr(std::unique_ptr<C>(new C{2020}));
r_take_ref_c(C{2020});
r_take_str(rust::Str("2020"));
r_take_empty_str(rust::Str{""});
r_take_empty_str(rust::Str{nullptr, 0});
#ifdef CXXBRIDGE_HAS_STRING_VIEW
r_take_str(std::string_view{"2020"});
r_take_empty_str(std::string_view{});
r_take_empty_str(std::string_view{""});
ASSERT(static_cast<std::string_view>(rust::Str{"2020"}) == "2020");
#endif
r_take_slice_char(rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA)));
r_take_rust_string(rust::String("2020"));
r_take_empty_rust_string(rust::String{""});
r_take_empty_rust_string(rust::String{nullptr, 0});
#ifdef CXXBRIDGE_HAS_STRING_VIEW
r_take_rust_string(std::string_view{"2020"});
r_take_empty_rust_string(std::string_view{});
r_take_empty_rust_string(std::string_view{""});
ASSERT(static_cast<std::string_view>(rust::String{"2020"}) == "2020");
#endif
r_take_unique_ptr_string(
std::unique_ptr<std::string>(new std::string("2020")));
r_take_ref_vector(std::vector<uint8_t>{20, 2, 0});
Expand Down