[−][src]Crate gobject_gen
Generates code to create a derived glib::Object
The gobject_gen!
macro defines an extension to the Rust language so
that one can create GObject implementations, or define
GTypeInterfaces, using only safe code. All the boilerplate needed
to register the GObject type, its signals and properties, etc., is
automatically generated.
Syntax overview {#syntax-overview}
The macro is invoked as follows:
// see "Necessary imports" below on why this is needed
#[macro_use]
extern crate glib;
use gobject_gen::gobject_gen;
gobject_gen! {
class Foo {
private_field: Cell<u32>,
another_field: RefCell<String>,
}
// Methods and signals; their order defines
// the ABI of your class
impl Foo {
pub fn a_static_method(&self) {
// self.get_priv() gives us access to the private
// fields declared in class Foo
do_something(self.get_priv().private_field.get());
}
virtual fn a_virtual_method(&self) {
// default handler implementation goes here
}
signal fn clicked(&self);
}
}
Read on for the details on how to use specific GObject features.
Necessary imports
The generated code depends on external crates:
- The
glib
crate and its macros. - The
gobject_gen
crate, declaringproc_macro
use.
You can put this at the top of your crate's main file:
#![feature(proc_macro)]
extern crate gobject_gen;
#[macro_use]
extern crate glib;
use gobject_gen::gobject_gen;
You need the following dependencies in Cargo.toml
:
[dependencies]
glib = "0.5.0"
glib-sys = "0.6.0"
gobject-sys = "0.6.0"
libc = "0.2"
Instance-private data
GObject classes defined through this macro can have instance-private data declared as struct fields inside the class.
-
Declaration: Declare struct fields inside
class Foo { ... }
-
Initialization: Implement the
Default
trait for your struct members, either with#[derive(Default)]
or with animpl Default
if its missing. Note that you have to do this for each type used across fields. When the generated code needs to initialize the instance-private data, it will do so by calling theDefault::default()
method and assign it to the internally private structure generated by the macro. -
Drop: When the GObject instance gets finalized, your private data will be
drop()
ed. You can provideimpl Drop
for any fields that need explicit resource management.
Declaring methods
Inside an impl MyClass
item, you can declare static methods
(cannot be overriden in derived classes), or virtual methods that
can be overriden:
impl MyClass {
pub fn my_static_method(&self, x: u32) -> String {
// implement your method here
}
virtual fn my_virtual_method(&self) -> usize {
// implement your method here
}
}
A pub
method is static and cannot be overriden in derived
classes. A virtual
method is assumed to be public and can be
overriden.
The first argument must always be &self
; gnome-class doesn't
support &mut self
. To have mutable fields in your instances,
you must use interior mutability with Cell
or RefCell
.
Unlike normal Rust code, method arguments must be bare identifiers
with their types, like x: u32
; they cannot be pattern-matched
captures.
FIXME: mention limitations on argument and return types
Declaring signals
Signals are declared in a similar way to methods, but using signal
instad of virtual
:
impl MyClass {
signal fn my_signal(&self, x: u32) {
// implement your default signal handler here
}
}
FIXME: mention limitations on argument and return types
Overriding methods from a parent class
You can use the impl ParentClass for Subclass
notation to override methods in your subclass:
class Foo {
}
impl Foo {
virtual fn hello(&self) {
println!("hello");
}
}
class Bar {
}
impl Foo for Bar {
virtual fn hello(&self) {
println!("overriden hello");
}
}
As of 2018/Nov/15, it is hard to chain up to the parent class from an overriden method. See issue #46 about this.
ABI considerations
The Application Binary Interface (ABI) of a GObject class is
defined by its virtual method table (vtable), which includes both virtual
methods and signals. In gnome-class, this means that the order of
method or signal items inside an impl MyClass
is significant:
adding, removing, or reordering the methods and signals will
change the ABI, since C code will see a MyClass
struct with
fields to function pointers in a different order.
Conventionally, GObject classes can reserve a number of empty
vtable slots for future expansion. In gnome-class, you can use
the reserve_slots
keyword:
impl Foo {
virtual fn one_virtual_method(&self) { ... }
reserve_slots(10)
}
In the example above, there will be 10 unused function pointers at
the end of the generated class struct. If you ever add another
method or signal, decrement the number passed to
reserve_slots()
.
Debugging aids and examining generated code
With gnome-class still under development, you may need to examine
the code that gets generated from the procedural macro. First,
create a directory called generated
under your crate's toplevel
directory. Then, put a generate
attribute for your class, like
this:
#[cfg(feature = "test-generated")]
include!("generated/foo-gen.rs");
#[cfg(not(feature = "test-generated"))]
gobject_gen! {
#[generate("generated/foo-gen.rs")]
class Foo {
}
}
Correspondingly, add this to Cargo.toml
to declare the test-generated
feature:
[features]
# If this feature is enabled, it executes the tests with
# the rust files generated during an earlier run.
test-generated = []
If you just cargo build
your code, then it will output the file
generated/foo-gen.rs
which you can examine. You can then edit
that file and rebuild with cargo build -- --features test-generated
- this will cause
the foo-gen.rs
to get included, instead of using "fresh" generated code.
Remember that changes to the generated code will be overwritten the next time the procedural macro runs! Don't forget to report a bug if gnome-class is generating incorrect code for you!
Macros
gobject_gen | Generates the code to create a derived glib::Object |