Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2a139b4

Browse files
committedNov 3, 2022
cxx: implement slice support for nostd
OVERVIEW: This patch adds experimental support for us to use `slices` in a nostd/freestanding environment, aim is to run c++ <=> cxx-rs <=> rust on a baremental target environment and use slices, with the intention of supporting more features later. In the application `Cargo.toml` file, the following feature cfgs are required: ``` [dependencies] cxx = { ... , default-features = false } [build-dependencies] cxx-build = { ... , default-features = false } ``` Then build with: ``` RUSTFLAGS='--cfg cxx_experimental_no_alloc' cargo build ``` and in the particular `.cpp` file you may need to declare the following ``` void __assert_func (const char *__a, int, const char *__b, const char *__c) { while (true) {} } void *__gxx_personality_v0; ``` FUNCTIONALITY: Currently, this only supports slices (outside of trivial features). TESTING: Tested by compiling: - cargo test (run existing tests) - cxx/demo/ running it. - compiling with a arm toolchain setup with cmake/corrosion and running the binary on QEMU arm with basic logic assertions [1]. Current testing has been done in the direction of C++ -> Rust with a simple callback test to C++. A simple test setup can be seen here [2]. TODO: - Get features such as `Results<Ok, Err>` working. - When we build for the none `std` case (no `default-features`), instead of creating a symlink to the original `cxx.h` file, we copy it over and define the macro to disable stdlib dependencies. Perhaps there's a better way to do this? NOTES: By default, all the standard features are enabled gaurded by the `#ifndef CXXBRIDGE1_RUST_STD`, so this **shoudn't** break anything. [1] https://github.com/twilfredo/qemu_misc/tree/master/bm_arm [2] https://github.com/twilfredo/qemu_misc/blob/master/bm_arm/main.cpp Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
1 parent c7060d4 commit 2a139b4

File tree

5 files changed

+83
-26
lines changed

5 files changed

+83
-26
lines changed
 

‎build.rs

+20-7
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,26 @@ use std::path::Path;
33
use std::process::Command;
44

55
fn main() {
6-
cc::Build::new()
7-
.file("src/cxx.cc")
8-
.cpp(true)
9-
.cpp_link_stdlib(None) // linked via link-cplusplus crate
10-
.flag_if_supported(cxxbridge_flags::STD)
11-
.warnings_into_errors(cfg!(deny_warnings))
12-
.compile("cxxbridge1");
6+
// Building without alloc, for a nostd target.
7+
if !cfg!(feature = "alloc") {
8+
cc::Build::new()
9+
.file("src/cxx.cc")
10+
.cpp(true)
11+
.cpp_link_stdlib(None) // linked via link-cplusplus crate
12+
.flag_if_supported(cxxbridge_flags::STD)
13+
.warnings_into_errors(cfg!(deny_warnings))
14+
.define("CXXBRIDGE1_RUST_STD", None) // Exclude all dependencies on std
15+
.compile("cxxbridge1");
16+
} else {
17+
// Building for STD with all features
18+
cc::Build::new()
19+
.file("src/cxx.cc")
20+
.cpp(true)
21+
.cpp_link_stdlib(None) // linked via link-cplusplus crate
22+
.flag_if_supported(cxxbridge_flags::STD)
23+
.warnings_into_errors(cfg!(deny_warnings))
24+
.compile("cxxbridge1");
25+
}
1326

1427
println!("cargo:rerun-if-changed=src/cxx.cc");
1528
println!("cargo:rerun-if-changed=include/cxx.h");

‎gen/build/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ repository = "https://github.com/dtolnay/cxx"
1313
rust-version = "1.48"
1414

1515
[features]
16+
default = ["std"]
1617
parallel = ["cc/parallel"]
1718
# incomplete features that are not covered by a compatibility guarantee:
1819
experimental-async-fn = []
20+
std = []
1921

2022
[dependencies]
2123
cc = "1.0.49"

‎gen/build/src/lib.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,29 @@ fn make_include_dir(prj: &Project) -> Result<PathBuf> {
385385
let include_dir = prj.out_dir.join("cxxbridge").join("include");
386386
let cxx_h = include_dir.join("rust").join("cxx.h");
387387
let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h");
388-
if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") {
389-
out::symlink_file(original, cxx_h)?;
390-
out::symlink_file(original, shared_cxx_h)?;
391-
} else {
392-
out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?;
388+
389+
if !cfg!(feature = "std") {
390+
// For a nonstd target, specify `#define CXXBRIDGE1_RUST_STD` to a copy of "include/cxx.h" and
391+
// don't create a symlink to the original header (which has all std features enabled).
392+
// This copy can now be included by a C++ file
393+
// compiled/linked with `-nostdlib`
394+
let mut new = "// Allow building for a nostd target by excluding std dependencies\n#define CXXBRIDGE1_RUST_STD\n"
395+
.to_string()
396+
.as_bytes()
397+
.to_vec();
398+
let mut byte_vec = gen::include::HEADER.as_bytes().to_vec();
399+
new.append(&mut byte_vec);
400+
let slice: &[u8] = &new;
401+
out::write(shared_cxx_h, slice)?;
393402
out::symlink_file(shared_cxx_h, cxx_h)?;
403+
} else {
404+
if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") {
405+
out::symlink_file(original, cxx_h)?;
406+
out::symlink_file(original, shared_cxx_h)?;
407+
} else {
408+
out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?;
409+
out::symlink_file(shared_cxx_h, cxx_h)?;
410+
}
394411
}
395412
Ok(include_dir)
396413
}

‎include/cxx.h

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
#pragma once
2-
#include <algorithm>
32
#include <array>
4-
#include <cassert>
53
#include <cstddef>
64
#include <cstdint>
7-
#include <exception>
85
#include <initializer_list>
6+
#include <stdexcept>
7+
#include <string>
8+
9+
#ifndef CXXBRIDGE1_RUST_STD
10+
#include <algorithm>
11+
#include <cassert>
12+
#include <exception>
913
#include <iosfwd>
1014
#include <iterator>
1115
#include <new>
12-
#include <stdexcept>
13-
#include <string>
1416
#include <type_traits>
1517
#include <utility>
1618
#include <vector>
19+
#endif // CXXBRIDGE1_RUST_STD
1720
#if defined(_WIN32)
1821
#include <basetsd.h>
1922
#else
@@ -30,6 +33,7 @@ template <typename T>
3033
class impl;
3134
}
3235

36+
#ifndef CXXBRIDGE1_RUST_STD
3337
#ifndef CXXBRIDGE1_RUST_STRING
3438
#define CXXBRIDGE1_RUST_STRING
3539
// https://cxx.rs/binding/string.html
@@ -151,6 +155,7 @@ class Str final {
151155
std::array<std::uintptr_t, 2> repr;
152156
};
153157
#endif // CXXBRIDGE1_RUST_STR
158+
#endif
154159

155160
#ifndef CXXBRIDGE1_RUST_SLICE
156161
namespace detail {
@@ -412,8 +417,10 @@ using isize = ssize_t;
412417
#endif
413418
#endif // CXXBRIDGE1_RUST_ISIZE
414419

420+
#ifndef CXXBRIDGE1_RUST_STD
415421
std::ostream &operator<<(std::ostream &, const String &);
416422
std::ostream &operator<<(std::ostream &, const Str &);
423+
#endif
417424

418425
#ifndef CXXBRIDGE1_RUST_OPAQUE
419426
#define CXXBRIDGE1_RUST_OPAQUE
@@ -465,14 +472,17 @@ using f32 = float;
465472
using f64 = double;
466473

467474
// Snake case aliases for use in code that uses this style for type names.
468-
using string = String;
469-
using str = Str;
470475
template <typename T>
471476
using slice = Slice<T>;
477+
#ifndef CXXBRIDGE1_RUST_STD
478+
using string = String;
479+
using str = Str;
472480
template <typename T>
473481
using box = Box<T>;
474482
template <typename T>
475483
using vec = Vec<T>;
484+
#endif
485+
476486
using error = Error;
477487
template <typename Signature>
478488
using fn = Fn<Signature>;

‎src/cxx.cc

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#include "../include/cxx.h"
22
#include <cstring>
3+
#ifndef CXXBRIDGE1_RUST_STD
34
#include <iostream>
45
#include <memory>
6+
#endif
57

68
extern "C" {
9+
#ifndef CXXBRIDGE1_RUST_STD
710
void cxxbridge1$cxx_string$init(std::string *s, const std::uint8_t *ptr,
811
std::size_t len) noexcept {
912
new (s) std::string(reinterpret_cast<const char *>(ptr), len);
@@ -62,6 +65,7 @@ bool cxxbridge1$str$from(rust::Str *self, const char *ptr,
6265
std::size_t len) noexcept;
6366
const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept;
6467
std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept;
68+
#endif
6569

6670
// rust::Slice
6771
void cxxbridge1$slice$new(void *self, const void *ptr,
@@ -73,6 +77,13 @@ std::size_t cxxbridge1$slice$len(const void *self) noexcept;
7377
namespace rust {
7478
inline namespace cxxbridge1 {
7579

80+
template <typename T>
81+
static bool is_aligned(const void *ptr) noexcept {
82+
auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
83+
return !(iptr % alignof(T));
84+
}
85+
86+
#ifndef CXXBRIDGE1_RUST_STD
7687
template <typename Exception>
7788
void panic [[noreturn]] (const char *msg) {
7889
#if defined(RUST_CXX_NO_EXCEPTIONS)
@@ -85,12 +96,6 @@ void panic [[noreturn]] (const char *msg) {
8596

8697
template void panic<std::out_of_range> [[noreturn]] (const char *msg);
8798

88-
template <typename T>
89-
static bool is_aligned(const void *ptr) noexcept {
90-
auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
91-
return !(iptr % alignof(T));
92-
}
93-
9499
String::String() noexcept { cxxbridge1$string$new(this); }
95100

96101
String::String(const String &other) noexcept {
@@ -377,6 +382,7 @@ std::ostream &operator<<(std::ostream &os, const Str &s) {
377382
os.write(s.data(), s.size());
378383
return os;
379384
}
385+
#endif
380386

381387
void sliceInit(void *self, const void *ptr, std::size_t len) noexcept {
382388
cxxbridge1$slice$new(self, ptr, len);
@@ -406,11 +412,13 @@ static_assert(sizeof(rust::isize) == sizeof(std::intptr_t),
406412
static_assert(alignof(rust::isize) == alignof(std::intptr_t),
407413
"unsupported ssize_t alignment");
408414

415+
#ifndef CXXBRIDGE1_RUST_STD
409416
static_assert(std::is_trivially_copy_constructible<Str>::value,
410417
"trivial Str(const Str &)");
411418
static_assert(std::is_trivially_copy_assignable<Str>::value,
412419
"trivial operator=(const Str &)");
413420
static_assert(std::is_trivially_destructible<Str>::value, "trivial ~Str()");
421+
#endif
414422

415423
static_assert(
416424
std::is_trivially_copy_constructible<Slice<const std::uint8_t>>::value,
@@ -448,6 +456,7 @@ static_assert(!std::is_same<Vec<std::uint8_t>::const_iterator,
448456
Vec<std::uint8_t>::iterator>::value,
449457
"Vec<T>::const_iterator != Vec<T>::iterator");
450458

459+
#ifndef CXXBRIDGE1_RUST_STD
451460
static const char *errorCopy(const char *ptr, std::size_t len) {
452461
char *copy = new char[len];
453462
std::memcpy(copy, ptr, len);
@@ -494,6 +503,7 @@ Error &Error::operator=(Error &&other) &noexcept {
494503
other.len = 0;
495504
return *this;
496505
}
506+
#endif
497507

498508
const char *Error::what() const noexcept { return this->msg; }
499509

@@ -513,9 +523,11 @@ struct PtrLen final {
513523
};
514524
} // namespace repr
515525

526+
#ifndef CXXBRIDGE1_RUST_STD
516527
extern "C" {
517528
repr::PtrLen cxxbridge1$exception(const char *, std::size_t len) noexcept;
518529
}
530+
#endif
519531

520532
namespace detail {
521533
// On some platforms size_t is the same C++ type as one of the sized integer
@@ -530,7 +542,7 @@ using isize_if_unique =
530542
typename std::conditional<std::is_same<rust::isize, int64_t>::value ||
531543
std::is_same<rust::isize, int32_t>::value,
532544
struct isize_ignore, rust::isize>::type;
533-
545+
#ifndef CXXBRIDGE1_RUST_STD
534546
class Fail final {
535547
repr::PtrLen &throw$;
536548

@@ -547,6 +559,7 @@ void Fail::operator()(const char *catch$) noexcept {
547559
void Fail::operator()(const std::string &catch$) noexcept {
548560
throw$ = cxxbridge1$exception(catch$.data(), catch$.length());
549561
}
562+
#endif
550563
} // namespace detail
551564

552565
} // namespace cxxbridge1
@@ -559,6 +572,7 @@ void destroy(T *ptr) {
559572
}
560573
} // namespace
561574

575+
#ifndef CXXBRIDGE1_RUST_STD
562576
extern "C" {
563577
void cxxbridge1$unique_ptr$std$string$null(
564578
std::unique_ptr<std::string> *ptr) noexcept {
@@ -790,3 +804,4 @@ inline namespace cxxbridge1 {
790804
FOR_EACH_RUST_VEC(RUST_VEC_OPS)
791805
} // namespace cxxbridge1
792806
} // namespace rust
807+
#endif // CXXBRIDGE1_RUST_STD

0 commit comments

Comments
 (0)
Please sign in to comment.