foreign_class!
foreign_class!
is the way to describe an entity that will be visible for the "foreign language" as a class.
The basic example is:
use super::{f2, Foo};
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;
});
Here Foo
may be struct or enum or a more complex type (see below).
The self_type can be omitted if there are no constructors and all fn do not accept self.
self_type
The self_type is used as a "neat" way to call methods of types inside "smart pointers". For example sometimes you can not use your struct or enum explicitly, because of the semantic of the "foreign language" does not allow it, then you can write:
foreign_class!(class CircularDepsA {
self_type CircularDepsA;
constructor CircularDepsA::java_new(_: &str) -> Arc<Mutex<CircularDepsA>>;
fn CircularDepsA::a(&self, b: &CircularDepsB) -> String;
});
And flapigen
will be instructed to generate code to call each method listed above
with lock().unwrap().method
syntax.
Also maybe you want export a trait
implementation as foregin_class
.
The flapigen
cannot work with Box<Trait>
, because it requires unstable/unsafe APIs to
convert the boxed trait into something that can be transferred over the FFI border,
but it can work with Box<Box<Trait>>
, so via self_type you can represent a boxed trait as class:
pub trait Interface {
fn f(&self, _: i32) -> i32;
fn set(&mut self, _: i32);
}
struct InterfaceImpl {
base: i32,
}
impl Interface for InterfaceImpl {
fn f(&self, x: i32) -> i32 {
self.base + x
}
fn set(&mut self, x: i32) {
self.base = x;
}
}
fn create_interface() -> Box<Box<dyn Interface>> {
Box::new(Box::new(InterfaceImpl { base: 17 }))
}
foreign_class!(class Interface {
self_type dyn Interface;
constructor create_interface() -> Box<Box<dyn Interface>>;
fn Interface::f(&self, _: i32) -> i32;
fn Interface::set(&mut self, x: i32);
});
Also it should be noted that flapigen
generates code that uses "Universal Function Call Syntax",
so methods can be fake methods and declared outside of impl blocks:
pub struct TestMethodNotMethod;
impl TestMethodNotMethod {
fn new() -> Self {
TestMethodNotMethod
}
}
fn method_not_method(_this: &TestMethodNotMethod) {}
foreign_class!(class TestMethodNotMethod {
self_type TestMethodNotMethod;
constructor TestMethodNotMethod::new() -> TestMethodNotMethod;
fn method_not_method(&self);
});
Access modifiers
By default, methods in generated class are public. You can change access level via protected and private keywords.
foreign_class!(
class Foo {
self_type Foo;
constructor Foo::new() -> Foo;
private constructor Foo::from_int(_: i32) -> Foo;
private fn Foo::private_f();
fn Foo::public_f();
protected fn Foo::protected_f();
}
);
Inline methods
It is also sometimes convenient to write short methods just inside foreign_class!
, for example to add a way to access
fields of a struct or to export a result of a Rust macro call:
foreign_class!(class TestFnInline {
fn int_to_str(a: i32) -> String {
format!("{}", a)
}
});
To reference self
in inline methods you should use the this
variable, because the inline method
is not the real one, it is just code block that gets included into the generated code:
pub struct Session {
name: String,
}
foreign_class!(
#[derive(SmartPtrCopy)]
class Session {
self_type Session;
constructor session_init(name: &str) -> Rc<RefCell<Session>> {
Rc::new(RefCell::new(Session {
name: name.into(),
}))
}
fn name(&self) -> &str {
&this.name
}
}
);
Methods aliases
Also you can create an alias for a function name:
fn Foo::f(&self, _: i32, _: i32) -> i32; alias calcF;
So it would be called by path with fn in Rust code, and you can call it via name with alias in foreign language.
This may be useful for example if you want to name functions in Java in camel case style, while you want to use snake case style in Rust.
Constructors
Constructors are Rust methods that mapped to constructors in terms of the "foreign" language.
Also constructors, more precisely, the return type of constructors can be used to
ask flapigen
to simplify calls of methods, see the self_type section for more details.
Sometimes you need a constructor, but you don't want allow the user to construct objects,
then you can use an empty constructor:
foreign_class!(
class GamepadId {
self_type GamepadId;
private constructor = empty;
fn GamepadId::value(&self) -> usize;
}
);
foreigner_code
flapigen also supports bypassing code generation:
foreign_class!(class TestPathAndResult {
self_type TestPathAndResult;
constructor TestPathAndResult::empty() -> Result<TestPathAndResult, String>;
constructor TestPathAndResult::new(path: &Path) -> Result<TestPathAndResult, String>;
fn TestPathAndResult::get_path(&self) -> String; alias getPath;
fn TestPathAndResult::get_boo(&self) -> Rc<RefCell<Boo>>; alias getBoo;
foreign_code " public int javaFunc() { return 17; }\n";
foreign_code r#"
public Boo[] testHandArrayReturn() { return do_testHandArrayReturn(this.mNativeObj); }
private static native Boo[] do_testHandArrayReturn(long me);
"#;
fn TestPathAndResult::get_foo_list(&self) -> Vec<Foo>;
fn TestPathAndResult::get_result_foo_list(generate_err: bool) -> Result<Vec<Foo>, String>;
});
After that you can implement the Java_com_example_TestPathAndResult_do_1testHandArrayReturn function yourself, useful for when flapigen cannot handle something automatically, or you want to do something special.
Doc comments
You can also add comments to generated code with Rust doc comments:
foreign_class!(
/// Class comment description for Foo.
#[derive(Clone)]
class Foo {
self_type Foo;
/// some text about the new function
///
/// ```
/// some markdown example in the text
/// ```
///
/// @param val - some number
/// @param name - more information
constructor Foo::new(val: i32, name: &str) -> Foo;
Derives
You can use "derive" syntax on the declaration of classes, in a similar way to the usage on "Rust" structs:
/// Class comment description for Foo.
#[derive(Clone)]
class Foo {
self_type Foo;
Usage of derives changes generated code in various ways.
For example, you can use Clone,Copy
to force the generation of a copy constructor
and operator=
in C++ case.
You can also use camelCaseAliases
to change names of all methods to camel case.