Crate cpp

source ·
Expand description

This crate cpp provides macros that allow embedding arbitrary C++ code.

Usage

This crate must be used in tandem with the cpp_build crate. A basic Cargo project which uses these projects would have a structure like the following:

crate
|-- Cargo.toml
|-- src
    |-- main.rs
|-- build.rs

Where the files look like the following:

Cargo.toml
[package]
build = "build.rs"

[dependencies]
cpp = "0.5"

[build-dependencies]
cpp_build = "0.5"
build.rs
extern crate cpp_build;
fn main() {
    cpp_build::build("src/main.rs");
}
main.rs
use cpp::cpp;

cpp!{{
    #include <iostream>
}}

fn main() {
    let name = std::ffi::CString::new("World").unwrap();
    let name_ptr = name.as_ptr();
    let r = unsafe {
        cpp!([name_ptr as "const char *"] -> u32 as "int32_t" {
            std::cout << "Hello, " << name_ptr << std::endl;
            return 42;
        })
    };
    assert_eq!(r, 42)
}

Build script

Use the cpp_build crates from your build.rs script. The same version of cpp_build and cpp crates should be used. You can simply use the cpp_build::build function, or the cpp_build::Config struct if you want more option.

Behind the scene, it uses the cc crate.

Using external libraries

Most likely you will want to link against external libraries. You need to tell cpp_build about the include path and other flags via cpp_build::Config and you need to let cargo know about the link. More info in the cargo docs.

Your build.rs could look like this:

fn main() {
    let include_path = "/usr/include/myexternallib";
    let lib_path = "/usr/lib/myexternallib";
    cpp_build::Config::new().include(include_path).build("src/lib.rs");
    println!("cargo:rustc-link-search={}", lib_path);
    println!("cargo:rustc-link-lib=myexternallib");
}

(But you probably want to allow to configure the path via environment variables or find them using some external tool such as the pkg-config crate, instead of hardcoding them in the source)

Limitations

As with all procedure macro crates we also need to parse Rust source files to extract C++ code. That leads to the fact that some of the language features might not be supported in full. One example is the attributes. Only a limited number of attributes is supported, namely: #[path = "..."] for mod declarations to specify an alternative path to the module file and #[cfg(feature = "...")] for mod declarations to conditionally include the module into the parsing process. Please note that the latter is only supported in its simplest form: straight-forward feature = "..." without any additional conditions, cfg! macros are also not supported at the moment.

Since the C++ code is included within a rust file, the C++ code must obey both the Rust and the C++ lexing rules. For example, Rust supports nested block comments (/* ... /* ... */ ... */) while C++ does not, so nested comments not be used in the cpp! macro. Also the Rust lexer will not understand the C++ raw literal, nor all the C++ escape sequences within literal, so only string literals that are both valid in Rust and in C++ should be used. The same applies for group separators in numbers. Be careful to properly use #if / #else / #endif, and not have unbalanced delimiters.

Macros

  • This macro is used to embed arbitrary C++ code.
  • This macro allows wrapping a relocatable C++ struct or class that might have a destructor or copy constructor, implementing the Drop and Clone trait appropriately.