Duplicate Content

This page serves the same purpose, and contains much of the same information, as Legacy Bindings.

Writing VAPI files

Writing a VAPI file is something not very common, but sometimes a Vala programmer will have to do it. An example is when needing to use a library that still doesn't have a VAPI file, or when mixing C and Vala source code.

A VAPI file has the same syntax that a Vala source file, but the classes, methods and other definitions have several code attributes that allow to specify things like to which C elements they map, or what header files to add...

Defining a simple function

Let's see a very simple example of a VAPI file for the sync system call:

[CCode (cheader_filename = "unistd.h")]
namespace SampleNameSpace {
    [CCode (cname = "sync")]
    void sync();
}

We see that the first thing to do is to define a namespace that will contain all our definitions. In this example we called it SampleNameSpace. Also we put a code attribute that specifies the header file to include when using this namespace. If it needs several header files, it is possible to use a comma as separator.

Now, inside the namespace we see the function definition. It has a code attribute to specify the C name to use. In this case this is a must, because by default, the C function names are composed by joining the namespace (converted from CamelCase to lower_case) and the function name. In this case, without the cname attribute, the Vala compiler would transform a vala call to SampleNameSpace.sync() into sample_name_space_sync().

Defining classes

Several functions work in an object-oriented fashion by using a structure as object and several functions that receives a pointer to that structure as methods. for these kind of APIs it is possible to emulate a true object-oriented architecture. Let's show it with an example extracted from the XCB VAPI file:

[CCode (lower_case_cprefix = "xcb_", cheader_filename = "xcb/xcb.h,xcb/xproto.h")]
namespace Xcb {

    [Compact]
    [CCode (cname = "xcb_connection_t", cprefix = "xcb_", free_function = "xcb_disconnect")]
    public class Connection {
        [CCode (cname = "xcb_connect")]
        public Connection (string? display = null, out int screen = null);

        public void flush ();
        public uint32 generate_id ();
    }
}

In this example we start defining the namespace, as previously explained. Then, inside, we define a Compact class named Connection. This will be the main class, and the code attributes will map it to xcb_connection_t, which is a structure defined in xcb/xcb.h. The free_function specifies the function to call when the object must be freed. This function should be used when the library doesn't use reference counting. If that's not the case, you should use the functions ref_function and unref_function, pointing them to the functions that increment and decrement the reference counter.

Also there is the cprefix attribute, that specifies the prefix to add to each method inside the class to generate the C function name. In this case, the methods void flush() and uint32 generate_id() would be mapped to the C functions void xcb_flush(struct xcb_connection_t *) and uint32 xcb_generate_id(struct xcb_connection_t *). Also, as we see, the Vala compiler presumes that the first parameter in the functions mapped as methods is the structure representing the object. In case that the structure is not the first parameter, it is possible to use the attribute instance_pos to specify where to add the instance parameter. This attribute is a double, and its value must be a point between two parameters. Let's say that our C function is:

    void a_function_name(int value1, int value2, struct *object, char *a_string);

The definition for the method would be:

    [CCode (cname="a_function_name",instance_pos=1.5)]
    void function(int value1, int value2, string a_string);

The value is 1.5 because the instance parameter must be between parameters 1 and 2.

Look that the constructor has an attribute to map it to the struct xcb_connection_t xcb_connect C function. Also, as we can see it is possible to use default parameters.

Of course, don't forget to mark the methods as public, to ensure that they are accessible from other parts of the code.

In case that a function can't be mapped directly using these attributes, is possible to map it as a private method and define a new public method with some code to call the true C function. An example: let's say that we have a C function with a prototype int test_function(struct *object, enum Type, void *data) that receives a void pointer and a parameter specifying if it points to a string or to an array of int32 elements. It would be more elegant to have two methods, one that receives an string, and another that receives an array of int32. To achieve it we can do:

    [CCode (cname = "test_function")]
    private int32 internal_test_function(type the_type, void *data);
    public int32 test_function_int32(uint32 *data) {
        return this.internal_test_function(Type.INT32,(void *)data);
    }
    public int32 test_function_string(string data) {
        return this.internal_test_function(Type.STRING,(void *)data.data);
    }

So we define a private VALA method called internal_test_function that maps to our C function, and then we define two public VALA methods that call the private method with the desired parameters. Of course, if the C function names assigned by VALA to the public methods clash with other function name in the library, just add a cname attribute to specify a new name.

ENUMs

ENUMs in Vala are similar to ENUMs in C. Let's port this C enum to a VALA enum:

enum enum_test {
    TEST_ELEMENT1,
    TEST_ELEMENT2,
    TEST_ELEMENT3
}

The VAPI code for this enum should be:

[CCode (cname = "enum_test", has_type_id = false, cprefix = "TEST_")]
public enum Test {
    ELEMENT1,
    ELEMENT2,
    ELEMENT3
}

The cname attribute specifies the enum name in C language, and the has_type_id specifies if this is a GType element or not.

By default, each element will be named as NAME_SPACE_ENUMNAME_ELEMENT (all in uppercase). Since the elements in our enum don't follow this format (because they don't include the namespace), we add a cprefix attribute to specify that to add before.

If an enum in C specifies values for some or all of its elements, you MUST NOT copy them to the VAPI file. The enum in the VAPI file is only a template to know how to map from VALA to C, so those values are not needed until the C compiler pass.

Typedefs

When a C library defines new types with typedef the can be used in Vala by defining them as a sympletype struct, which inherits from the simple type specified in the typedef:

An example from XCB:

    typedef uint32_t xcb_atom_t;

is defined in the VAPI file as:

    [SimpleType]
    [CCode (cname = "xcb_atom_t", has_type_id = false)]
    public struct AtomT : uint32 {
    }

Constants

If a C library defines constants with the #define statement, they must be defined as public const TYPE.

An example:

#define XCB_GRAPHICS_EXPOSURE 13
#define XCB_NO_EXPOSURE 14
#define XCB_VISIBILITY_NOTIFY 15

is defined as:

public const uint8 GRAPHICS_EXPOSURE;
public const uint8 NO_EXPOSURE;
public const uint8 VISIBILITY_NOTIFY;

In this case we use uint8 because in the XCB library those defines are used as uint8 values. In other cases you should use uint32 or whatever other type needed.

More attributes

This is only an introduction to how to create VAPI files. Of course each library is a world, so there are a lot of corner cases that require other attributes.

A complete list of the code attributes available in Vala for the VAPI files can be found at https://wiki.gnome.org/Projects/Vala/Manual/Attributes

Projects/Vala/WrittingVAPIs (last edited 2014-01-06 10:55:06 by SergioCostas)