rust-cpp

Build Status Build status

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.

Setup

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. }); } ```

Usage

In your crate, include the cpp crate macros:

```rust

[macro_use]

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 #include "foo.h"

// 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++

ifndef RUSTTYPESH

define RUSTTYPESH

include

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");

}

endif

```

Warning about Macros

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.