[][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:

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.

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