For this example, let's see if we can use Vala to manilulate a gdbm database.
Gdbm is a tiny db used in lots of applications.
In order for a C library to work in Vala, it needs a .vapi file to translate.
In this example, we will use the gdbm library (a small, common database in C) using Vala bindings.
There are two issues for new users here:
- Linking to C libraries in the C compiler
- Linking to vapi files in the Vala interpreter
1) Download the sources:
- Create a working directory
- Install the libgdbm-dev package, to get the c headers.
- I discovered a vapi file for gdbm created by Andre Masella. Thank you, Andre!
- Download and install the vapi file. Vapi files belong in /usr/share/vala/vapi/
- Open a browser window to the Gnu GDBM documentation.
$ mkdir /home/me/vala/gdbm_vala $ cd /home/me/vala/gdbm_vala # sudo apt-get install libgdbm-dev $ wget https://raw.github.com/apmasell/vapis/master/gdbm.vapi # sudo ln /home/me/vala/gdbm_vala/gdbm.vapi /usr/share/vala/vapi/
2) Create a simple file in C to test the gdbm library:
It gets the gdbm_version string from the gdbm library.
#include <stdio.h> #include <gdbm.h> int main() { printf("VERSION (C): %s.\n", gdbm_version); }
- The gcc -o flag specifies the output file name
$ gcc -o gdbm_version1 gdbm_version1.c /tmp/cc31QKu0.o: In function `main': gdbm_version1.c:(.text+0xa): undefined reference to `gdbm_version' collect2: error: ld returned 1 exit status
Uh-oh. Fatal error.
The compiler did not understand the 'gdbm_version' variable because gdbm.h is not in it's standard library. I must tell the compiler to link to it using the -l flag.
Try again, linking to the gdbm library.
$ gcc -o gdbm_version1 gdbm_version1.c -l gdbm $ ./gdbm_version1 VERSION (C): GDBM version 1.8.3. 10/15/2002 (built Jun 11 2012 00:27:26).
There should be no errors or warnings. Success!
3) First program:
Here's the relevant entry in the vapi file for the version number:
[CCode(cheader_filename = "gdbm.h")] namespace GDBM { ... [CCode(cname = "gdbm_version")] public const string VERSION; ... }
Namespace GDBM + string VERSION, so "string GDBM.VERSION" in Vala should translate to "string gdbm_version" in C. And we just tested that the C version works.
Let's try a simple Vala program that checks the version number:
private static void main () { stdout.printf("The gdbm version string: %s\n", GDBM.VERSION); }
And compile it:
- --pkg gdbm tells valac to look for the gdbm vapi file.
$ valac --verbose --pkg gdbm gdbm_version2.vala Loaded package `/usr/share/vala-0.18/vapi/glib-2.0.vapi' Loaded package `/usr/share/vala-0.18/vapi/gobject-2.0.vapi' Loaded package `/usr/share/vala/vapi/gdbm.vapi' cc -o '/home/ian/vala/gdbm_vala/code/gdbm_version2' '/home/ian/vala/gdbm_vala/code/gdbm_version2.vala.c' -I/usr/include/glib-2.0 -I/usr/lib/i386-linux-gnu/glib-2.0/include -lgobject-2.0 -lglib-2.0 /tmp/ccnYcsLH.o: In function `_vala_main': gdbm_version2.vala.c:(.text+0xf): undefined reference to `gdbm_version' collect2: error: ld returned 1 exit status error: cc exited with status 256 Compilation failed: 1 error(s), 0 warning(s)
Uh-oh. Something went wrong.
When something goes wrong , the --verbose flag is your friend.
The output shows that the Vala was translated into C without any warnings or errors, but then something was wrong when the compiler tried to prcoess the C.
Wait a second...it's the SAME ERROR we had before in the C program!
We know how to fix that: Tell the compiler to link (-l flag) to the gdbm library.
Check the compiler command, and --sure enough-- link to the gdbm library is missing.
Two lessons here:
- Vala's link to a pkg does not necessarily mean the compiler is linked to the library.
valac --pkg foo
may not translate intocc -lfoo
- Use valac's
-Xcc
flag to pass links to the compiler.
Let's try compiling again, adding /usr/include/gdbm.h using an Xcc flag, and removing --verbose:
- --pkg gdbm tells valac to use the gdbm vapi file
- --Xcc=-gdbm tells the C compiler to link to the C gdbm header
$ valac --pkg gdbm --Xcc=-lgdbm gdbm_version2.vala $ ./gdbm_version2 The gdbm version is: GDBM version 1.8.3. 10/15/2002 (built Jun 11 2012 00:27:26)
No errors or warnings. Success!
4) Take a break
We are now past the a bunch of biggest beginner hurdles:
- How to figure out common compile errors.
- How to tell the compiler to link to libraries.
- How to tell valac to use vapi files.
5) More complicated program
Here is a more complicated program that opens a test database (or create a new db if it doesn't exist), and look for a certain key. If the key is not in the database, it appends the key:value pair to the database.
- It uses the gdbm.vapi bindings for open(), read/write/create permission, contains(), and save() [which really means append].
// File: /home/me/vala/gdbm_vala/code/gdbm_add private static int main () { // Hard-coded values string filename = "/home/me/vala/gdbm_vala/code/sample_db.gdbm"; string key_string = "spam"; string value_string = "eggs"; // Open the database // GDBM.OpenFlag.WRCREAT comes from the vapi file. It means read+write+create new db GDBM.Database db = GDBM.Database.open (filename, 0, GDBM.OpenFlag.WRCREAT); // Convert the string values to bytes. gdbm doesn't save strings. uint8[] key_int = key_string.data; uint8[] value_int = value_string.data; // If the key_int is already in the database, say so and exit. if (db.contains (key_int) == true) { stdout.printf("In db\n"); return 0; } stdout.printf("Not in db\n"); // If appending the key:value pair to the database is successful, say so and exit. if (db.save (key_int, value_int, false) == true) { stdout.printf("Good append saved\n"); return 0; } // Problems stdout.printf("Failed to append\n"); return 1; }
Let's compile it.
$ $ valac --pkg gdbm --Xcc=-lgdbm gdbm_add.vala /home/ian/vala/gdbm_vala/code/gdbm_add.vala.c: In function ‘_vala_main’: /home/ian/vala/gdbm_vala/code/gdbm_add.vala.c:192:2: warning: passing argument 1 of ‘gdbm_open’ discards ‘const’ qualifier from pointer target type [enabled by default] In file included from /home/ian/vala/gdbm_vala/code/gdbm_add.vala.c:9:0: /usr/include/gdbm.h:85:18: note: expected ‘char *’ but argument is of type ‘const gchar *’
Oh, my.
Let's look more closely at these error messages. Each error takes two lines
- The first warning is in the generated C file, line 192,
warning: passing argument 1 of ‘gdbm_open’ discards ‘const’ qualifier from pointer target type [enabled by default]
.
Line 192 of the C file looks like_tmp4_ = gdbm_open (_tmp3_, 0, GDBM_WRCREAT, 0644, NULL);
which looks a lot like the original gdbm_open function again.
Valac is moving the filename to _tmp3_, and that _tmp_ variable seems to be the wrong type. It's just a warning - the code still compiles. And it seems to be a bug in valac, not a problem with our code.
- The second warning is in the generated C file, line 9. That's the
#include
line. The warning isexpected ‘char *’ but argument is of type ‘const gchar *’
This is an interesting warning, and I'm not sure my merely including the header would trigger it...but it's also just a warning, and the compiled binary works.
$ ./gdbm_add Not in db Good append saved $ ./gdbm_add In db $ ./gdbm_add In db
It works!
We've gotten past the namespace hurdle, the vapi hurdle, and have successfully manipulated a gdbm database using the original C library.
No comments:
Post a Comment