rust-cpp is a build tool & macro which enables you to write C++ code inline in your rust code.
NOTE: This crate works on stable rust, but it is not stable itself. You can use this version all you want, but don't be surprised when a 0.2 release is made which completely breaks backwords compatibility. I view this crate as more of an experiment than a product.
As the tools come into stable rust to make this more practical to use, I expect that it will stabilize. Namely, I do not expect that this module will have a stable-ish interface until we get a stable procedural macro system.
NOTE: As the stable branch of rust-cpp is not on crates.io, you will have to download it and use the path manually. This will likely be changed in the future.
Add cpp
as a dependency to your project. It will need to be added both as a
build dependency, and as a normal dependency, with different flags. You'll also
need a build.rs
set up for your project.
```toml [package]
build = "build.rs"
[build-dependencies]
cpp = { version = "0.1.0", features = ["build"] }
[dependencies]
cpp = { version = "0.1.0", features = ["macro"] } ```
You'll also then need to call the cpp
build plugin from your build.rs
. It
should look something like this:
```rust extern crate cpp;
fn main() { cpp::build("src/lib.rs", "crate_name", |cfg| { // cfg is a gcc::Config object. You can use it to add additional // configuration options to the invocation of the C++ compiler. }); } ```
In your crate, include the cpp crate macros:
```rust
extern crate cpp; ```
Then, use the cpp!
macro to define code and other logic which you want shared
between rust and C++. The cpp!
macro supports the following forms:
``rust
cpp! {
// Include a C++ library into the C++ shim. Only the
#include` directive
// is supported in this context.
#include
// Write some logic directly into the shim. Either a curly-braced block or
// string literal are supported
raw {
#define X 10
struct Foo {
uint32_t x;
};
}
raw r#"
#define Y 20
"#
// Define a function which can be called from rust, but is implemented in
// C++. Its name is used as the C function name, and cannot collide with
// other C functions. The body may be defined as a curly-braced block or
// string literal.
// These functions are unsafe, and can only be called from unsafe blocks.
fn my_function(x: i32 as "int32_t", y: u64 as "uint32_t") -> f32 as "float" {
return (float)(x + y);
}
fn my_raw_function(x: i32 as "int32_t") -> u32 as "uint32_t" r#"
return x;
"#
// Define a struct which is shared between C++ and rust. In C++-land its
// name will be in the global namespace. In rust it will be located
// wherever the cpp! block is located
struct MyStruct {
x: i32 as "int32_t",
y: *const i8 as "const char*",
}
// Define an enum which is shared between C++ and rust. In C++-land it
// will be defined in the global namespace as an `enum`. In rust,
// it will be located wherever the cpp! block is located.
enum MyEnum {
A, // Known in C++ as `A`
B,
C,
D,
}
// Enums can also be declared as `enum class` or `enum prefix` which will
// cause them to be defined in C++-land as either an `enum class` or an
// enum with each of its members prefixed with `EnumName_`.
enum class MyEnumClass {
A, // Known in C++ as `MyEnumClass::A`
B,
C,
D,
}
enum prefix MyEnumPrefix {
A, // Known in C++ as `MyEnumPrefix_A`
B,
C,
D,
}
} ```
cpp
also provides a header which may be useful for interop code. This header
includes <cstdint>
, which means that a sufficiently modern C++ compiler may be
required to use it. This header, rust_types.h
, can be included with:
rust
cpp! {
#include "rust_types.h"
}
It provides the rs
namespace, which contains various type definitions for rust
types, such as the numeric types (rs::i8
, rs::u64
, rs::f32
, rs::usize
,
etc.), the rust slice type (&[u8] => rs::Slice<rs::u8>
), and the rust trait
object fat pointer type. There are also definitions for rs::bool_
, which is
guaranteed to be 1 byte wide, the size of a rust bool
, and rs::char_
which
is the size of a rust char
. The full body of rust_types.h
is included below.
```c++
namespace rs { typedef int8t i8; staticassert(sizeof(i8) == 1, "int is the right size"); typedef int16t i16; staticassert(sizeof(i16) == 2, "int is the right size"); typedef int32t i32; staticassert(sizeof(i32) == 4, "int is the right size"); typedef int64t i64; staticassert(sizeof(i64) == 8, "int is the right size"); typedef intptr_t isize;
typedef uint8_t u8;
static_assert(sizeof(u8) == 1, "int is the right size");
typedef uint16_t u16;
static_assert(sizeof(u16) == 2, "int is the right size");
typedef uint32_t u32;
static_assert(sizeof(u32) == 4, "int is the right size");
typedef uint64_t u64;
static_assert(sizeof(u64) == 8, "int is the right size");
typedef uintptr_t usize;
typedef float f32;
static_assert(sizeof(f32) == 4, "float is the right size");
typedef double f64;
static_assert(sizeof(f64) == 8, "float is the right size");
typedef u8 bool_;
static_assert(sizeof(bool_) == 1, "booleans are the right size");
typedef uint32_t char_;
static_assert(sizeof(char_) == 4, "char is the right size");
}
```
rust-cpp cannot identify and parse the information found in cpp! blocks which
are generated with macros. These blocks will correctly generate rust code, but
will not generate the corresponding C++ code, most likely causing your build to
fail with a linker error. Do not create cpp! {}
blocks with macros to avoid
this.