C++
To run the cpp-example from here you need boost, cmake and a C++11 compatible compiler.
Project Structure
The projects consists of two parts: cpp-part
and rust-part.
The Rust part is compiled as shared library and linked into executable
described by CMakeLists.txt.
The cpp-part is the main part so its build system (cmake) invokes cargo to build the Rust part.
Building
It is a normal CMake project, so you can build it as an ordinary CMake project. By default it requires C++11 and boost, but if your compiler is modern enough you can use C++17 and then you don't need boost at all.
Just delete all mentions of boost here:
// build.rs
let cpp_cfg = CppConfig::new(
Path::new("..").join("cpp-part").join("rust-api"),
"rust".into(),
)
.cpp_optional(CppOptional::Boost)
.cpp_variant(CppVariant::Boost)
.cpp_str_view(CppStrView::Boost);
The main functionality
This project demonstrates how to export Rust in the form of a class to C++.
Rust code:
// src/lib.rs
pub struct Foo {
data: i32,
}
impl Foo {
fn new(val: i32) -> Foo {
Foo { data: val }
}
fn f(&self, a: i32, b: i32) -> i32 {
self.data + a + b
}
fn set_field(&mut self, v: i32) {
self.data = v;
}
}
Described as class:
// src/cpp_glue.rs.in
foreign_class!(class Foo {
self_type Foo;
constructor Foo::new(_: i32) -> Foo;
fn Foo::set_field(&mut self, _: i32);
fn Foo::f(&self, _: i32, _: i32) -> i32;
fn f2(_: i32) -> i32;
});
Usage from C++:
// main.cpp
Foo foo(5);
int res = foo.f(1, 2);
Borrowed foreign classes
For a regular foreign_class!, Rust &Foo arguments and return values use
FooRef by value in C++. A Foo or const Foo& converts implicitly to
FooRef, so either an owning object or an existing borrowed view can be passed
to a generated method. For example, the C++ test fixture
uses TestReferences and Foo:
TestReferences source{1, "source"};
TestReferences target{2, "target"};
FooRef view = source.get_foo_ref();
target.update_foo(view);
const Foo owned{3, "owned"};
target.update_foo(owned);
Copying a FooRef copies only its pointer. Keep the Rust object alive while
using the view. Rust &mut Foo arguments still take Foo& in C++; a returned
&mut Foo currently becomes a read-only FooRef.
A plain C++ class
By default, flapigen generates two C++ variants for a foreign_class!: an
owning class and a non-owning reference class. If you don't want to deal with two variants and
the need to convert one into the other in various cases, #[derive(PlainClass)] generates just
one C++ class. By default, returning a Rust reference to that type
(&ScoreAdjustment or &mut ScoreAdjustment) gives a compile error because
there is no C++ reference wrapper. Return an owned value instead, or define an
explicit outgoing foreign_typemap! for a custom borrowed C++ view. The C++
caller must keep the Rust referent alive while using such a view. The single
class can also be forward-declared as
class ScoreAdjustment; in a handwritten C++ header.
The Rust type and its binding are:
pub struct ScoreAdjustment {
bonus: i32,
}
impl ScoreAdjustment {
fn new(bonus: i32) -> Self {
Self { bonus }
}
fn apply(&self, base: i32) -> i32 {
base + self.bonus
}
}
foreign_class!(
#[derive(PlainClass)]
class ScoreAdjustment {
self_type ScoreAdjustment;
constructor ScoreAdjustment::new(bonus: i32) -> ScoreAdjustment;
fn ScoreAdjustment::apply(&self, base: i32) -> i32;
}
);
The C++ helper header can now declare a function using the generated class:
namespace rust { class ScoreAdjustment; }
int adjusted_score(const rust::ScoreAdjustment& adjustment, int base);
The C++ implementation includes ScoreAdjustment.hpp before calling its
method:
int adjusted_score(const ScoreAdjustment& adjustment, int base)
{
return adjustment.apply(base);
}
The executable uses the helper like this:
ScoreAdjustment adjustment(3);
if (adjusted_score(adjustment, 5) != 8) {
return EXIT_FAILURE;
}