The Vala documentation is pretty poor around this area in my opinion, it focuses mainly on mixing Vala with Glib C based libraries rather than basic C object files. However, as it turns out, its pretty easy to do.
Say for example, we have our C library in a folder named c; there are two files: c/functions.h and c/functions.c.
/* c/functions.h */ int foo(int a, int b); const char * hello();
/* c/functions.c */
#include "functions.h"
int foo(int a, int b)
{
return a * b;
}
const char * hello()
{
char * s = "Hello from pure C!\n";
return s;
}
Firstly, we need to compile a object file which contains the above functions which can then be used via Vala.
gcc -c c/functions.c -o c/functions.o
Now thats done, we can move onto creating a VAPI file which is similar to a C header file, it contains the same prototypes as functions.h, but in Vala syntax.
/* functions.vapi */
[CCode(cheader_filename = "functions.h")]
namespace functions {
[CCode(cname = "foo")]
int foo(int a, int b);
[CCode(cname = "hello")]
unowned string hello();
}
The VAPI file makes use of several attributes (in a way similar to how C# does), for example, cname="foo" is simply what the function is referred to in c/functions.h, which allows us to change the name exposed to Vala for greater OOP clarity if necessary.
The method modifier unowned is used as an equivalent to C’s const. Try omitting unowned and see what happens when you run the end program, bonus points if you can explain why
.
Moving on to the main program, which should be self explanatory:
/* main.vala */
using functions;
public static int main(string[] args)
{
stdout.printf("\nfoo(7, 6): %d\n", foo(7, 6));
stdout.printf("hello(): %s\n", hello());
return 0;
}
Almost there now, we just need to link it all together using the Vala compiler. Open you favorite terminal and enter the following:
valac -X "-Ic/" -X "c/functions.o" --vapidir=. --pkg functions main.vala
A quick explanation of the above and we’re done…
The -X flags specify arguments to pass to the C compiler: we need to pass the include directory of our functions.h, which is the -l flag and we also need to specify the object file which contains our C functions (c/functions.o).
Next up is the --vapidir parameter which simply points to the folder with our VAPI file in it. We specify which VAPI file to load using the --pkg functions parameter. As you can probably guess the pkg name must match the base name of it’s respective VAPI file.
Now finally, the end program can be run:
$ ./main
foo(7, 6): 42
hello(): Hello from pure C!