Vala: Bindings for non-GObject type C libraries

Feb 18th
Posted by Michael Trausch  as computing

Recently, I came upon Vala, the new programming language that has been created which is similar in nature to C#, but compiles to C (and thus to an efficient native executable). This is the kind of interesting thing that can come from research with managed code systems—and like any new technology that someone gets the idea to create, it was (at least to me) quite unexpected. Essentially, Vala provides reachability to programmers so that they can write good quality software in an easily readable and maintainable fashion. The Vala compiler is not unlike something like GCC; it takes source code input and generates code output. In this case, instead of the output being assembler, as is traditionally done with native languages, the output is C which can be fed to a C compiler like GCC, when then turns it to assembly and creates the executable.

Vala supports an object-oriented means of programming. It performs “assisted” memory management, and handles a lot of things which are tedious to do in C directly for you. This is in contrast to a runtime system like the JVM or the CLR, where the entire program requires this runtime engine and a (usually pretty large) standard library. These things do full memory management by way of garbage collection, which is not a horrible idea, but that, plus the extra layers of software can slow things down. (Not that they have to: an entire operating system written in Java or CLI would have zero cost of having child processes which are Java or CLI processes; instead, native executables would require emulation and a performance hit. However, this is tangential…)

Getting back on track, Vala is targeted at the GObject type system. This means that programs that are written in Vala do require some libraries at run-time, namely the GLib and GLib-Object libraries. These libraries are part of the LSB spec, and are installed on most reasonable and modern systems, with possible exception for systems configured to be servers. Effectively, though, the cost is nothing. This brings me to the point I originally wanted to write about: using libraries in Vala.

In order to use a library in Vala, there must be a .vapi file for it. This VAPI file is essentially a description of the library; it provides Vala with information it uses to then call the entrypoints in the file. The VAPI file is also very powerful: You can write one to describe a regular C library that doesn’t use GObject, and it provides assistance to be able to “objectify” that library if you want. Creating this bindings is a manual process, but it isn’t terribly hard—though Vala documentation is, at the moment, lacking a bit in terms of consumer-oriented documentation. As is the case with free software, when user documentation doesn’t exist, we can always refer to the source code for additional detail and clarification if necessary. (And this isn’t terribly hard to do with the Vala compiler, it’s written in Vala!)

In the VAPI file, you can define one or more namespaces, and then enumerations, constants, classes, methods, etc., within those namespaces.


The Example

For this example, we’ll use the libuuid library, which comes with e2fsprogs. On Ubuntu, ensure that you have the development headers installed by executing sudo apt-get install uuid-dev prior to getting started—you’ll need those headers installed in order to be able to build software using the bindings. Then, create yourself a working directory; ~/uuid-vala is what I’ll be using in this example. We’ll go through a few different iterations of this binding, progessively adding functionality and usefulness to it. I’ll take you through the steps that I went through to generate this VAPI file, sans a few emails to the Vala emailing list. :-)

A good starting point for creating a binding in Vala is API documentation; in the case of libuuid, there are the man pages uuid, uuid_clear, uuid_compare, uuid_copy, uuid_generate, uuid_is_null, uuid_parse, uuid_time, and uuid_unparse. From here, we can get the function prototypes that we need—the library’s entrypoints. What we don’t get from them is the definitions of the data types and some of the other data. For that, we refer to the header file—stated in the man pages as uuid/uuid.h. The header gives us the rest of the information that we know; we need the definitions for uuid_t, UUID_VARIANT_* and UUID_TYPE_DCE_*. Also, because we don’t have a complete set of class libraries/bindings wrapping basic POSIX functionality, we need to import time_t and struct timeval.

One thing to consider here before we actually write the VAPI file is which types are opaque for the purposes of the bindings, and which ones are transparent. Again, if we had a complete set of class libraries/bindings, this wouldn’t be a problem—we’d just use the existing types. For now, we’ll assume that the uuid_t type is opaque, and that we need to use the time_t and struct timeval types, but we’ll not be wrapping them in this first go-around.

Without futher ado, here’s the first iteration of the VAPI file, with commentary and explanation following:

/*
 * libuuid.vapi - Vala bindings for the e2fsprogs library libuuid
 * Copyright (c) 2009 Michael B. Trausch <mike@trausch.us>
 * License: GNU LGPL v3 as published by the Free Software Foundation
 *
 * This binding is a (mostly) strict binding to the function-oriented
 * nature of this C library.
 */

[CCode(cheader_filename = "uuid/uuid.h",
	   lower_case_cprefix = "uuid_", cprefix = "")]
namespace LibUUID {
	[CCode(cprefix = "UUID_VARIANT_")]
	public enum Variant {
		NCS,
		DCE,
		MICROSOFT,
		OTHER
	}

	[CCode(cprefix = "UUID_TYPE_DCE_")]
	public enum Type {
		TIME = 1,
		RANDOM = 4
	}

	[CCode(cname = "time_t")]
	public struct Time : int{}

	[CCode(cname = "struct timeval")]
	public struct TimeValue {
		[CCode(cname = "tv_sec")]
		public int seconds;

		[CCode(cname = "tv_usec")]
		public int microseconds;
	}

	[SimpleType]
	[CCode(cname = "uuid_t")]
	public struct UUID : char {}

	public void clear(UUID uu);
	public int compare(UUID uu1, UUID uu2);
	public void copy(UUID destination, UUID source);
	public void generate(UUID output);
	public void generate_random(UUID output);
	public void generate_time(UUID output);
	public int is_null(UUID uu);
	public int parse(string uuid_input, UUID uu);

	[CCode(cname = "uuid_time")]
	public Time get_time(UUID uu, out TimeValue time_ret);
	public void unparse(UUID uu,
						[CCode(array_length = false,
							   array_nullterminated = false)]
						char[37] uuid_asciiz);
	public void unparse_upper(UUID uu,
							  [CCode(array_length = false,
									 array_nullterminated = false)]
							  char[37] uuid_asciiz);
	public void unparse_lower(UUID uu,
							  [CCode(array_length = false,
									 array_nullterminated = false)]
							  char[37] uuid_asciiz);

	// Not documented in the man page, but exposed in the headers.
	[CCode(cname = "uuid_type")]
	public int get_type(UUID uu);

	[CCode(cname = "uuid_variant")]
	public int get_variant(UUID uu);

	// Part of the C library, but provided to make life easier on an
	// application programmer.
	[CCode(cname = "ctime", cheader_filename = "time.h")]
	public weak string get_unixtime_to_string(ref LibUUID.Time seconds);
}

As I mentioned earlier, Vala is a language that is similar in many ways to the C# language. If you’ve worked with C# code, you’ll recognize the [CCode …] immediately prior to the namespace LibUUID declaration as an attribute. The attribute looks similar to a function call—and while I haven’t gotten terribly deep into Vala, I have the feeling that Vala doesn’t treat attributes as function calls. After all, this isn’t the CLR that we’re talking about, where attributes are really classes, and instantiations of them are special ways of instantiating them and attaching them to classes. Anyway, this particular attribute contains three parameters:

  • cheader_filename = "uuid/uuid.h" – This attribute informs Vala that the bindings come from the specified header file. This attribute can be applied to (at least) namespaces and methods.
  • lower_case_prefix = "uuid_" – This attribute informs Vala that the methods are prefixed with uuid_ in C code. Looking down a bit, this means that public void clear(UUID uu); is really void uuid_clear(uuid_t uu) when called from C.
  • cprefix = "" – This states that we don’t want to use a C prefix for anything else. Between this and the previous parameter, this means that if we have to bind something that doesn’t fit the naming conventions that we’ve already laid out, we’ll have to manually define them when we define the item in question.

The next thing after the namespace declaration is the the enum declaration for Variant. This enum maps the series of constants in the header file which define the UUID_VARIANT type. The same goes for Type, for UUID_TYPE. The cprefix parameter to the attached CCode attribute says that these enums are mapped to constants defined in the header file, and it will then generate C code which references those constants (which will then be “expanded” by the preprocessor when the C compiler gets to the source code).

After these, we find some type definitions that make the binding a little bit easier to use. Specifically, for time_t, struct timeval, and uuid_t. These definitions provide some help with type-safety in Vala, and are terribly useful.

Now, we are down to the methods. The first 8 methods are regular, in that they follow all of the previously declared rules. As an example, a call to LibUUID.clear(uu) (where uu is a UUID data type) will expand to uuid_clear(uu) in C code. Since clear returns void, Vala doesn’t have to generate any code to handle the return value. In the case of compare, it will of course assign the return value to a variable in C and support you working with it later. The first non-regular function here is get_time, which has the real name uuid_time, not uuid_get_time. I renamed it to make more sense in the context of writing new code in Vala, though this was an optional choice.

Next, we see the unparse family of functions. These take a binary UUID and expand them to a string such that you can use the UUID in your user interface or when passing code to a database server or other external program. This was challenging, and it is the most fragile part of the binding. In other words, what I did here works, but it’s really not good practice to do generally. This is an example of a fairly easy-to-implement binding. I wouldn’t recommend distribution of the binding in production code, because you can still shoot yourself in the foot with it—the caller is tasked with the burden of creating a char buffer long enough to hold the return value, and if they don’t, it’s pretty likely that glibc will kill the program at run time with the message “buffer overrun detected” and a very verbose backtrace and memory mapping dump.

The unparse family takes a UUID as input, and a char buffer as output. Because the library assumes that it will be passed a 37 byte buffer, it doesn’t have any parameters to specify the length and will go ahead and blindly overwrite whatever you may have in the buffer for the first 37 bytes (36 bytes hold the UUID, and the last byte holds the trailing NULL byte, as is common in C when dealing with strings). We have the array_nullterminated parameter set to false because this is really an output parameter, though we’ve not told Vala that. Why? Because the UUID library deals with char[] buffers and char* strings, and when you pass those in C, you’re passing them by reference anyway. You only have to explicitly pass the address of them if you’re using statically-allocated storage (in C, that is). And even things that appear static (uuid_t type) aren’t because of the underlying type being a pointer to a char buffer. Confused yet? Ahh, the reasons I try to stay away from C most days…

Anyway, more of the same for the other unparse functions.

Now, we have those variant and type enumerations, but nothing in the man pages talked about using them; that’s fine, of course, since we got them from the header. There were two other functions in the header, which we have mapped as get_type and get_variant, which can be used to get information on the UUID being worked with.

Finally, we have an import from time.h. The reason that this is here, instead of leaving it up to the application programmer to provide it, is because of the way Vala seems to treat external references. It seems to want to create a prototype for them if you declare them in a .vala file, but it doesn’t seem to do that when declared in a .vapi file. Since it’s helpful to not have a fatal compiler error choking on conflicting prototypes, we’ve provided an interface to ctime() directly in this VAPI implementation.

So, is this a good binding or not? Well, let’s take a look at a sample program that uses the binding, and judge from that:

/*
 * uuid-fo.vala - Test for libuuid.vapi bindings for libuuid.
 * Copyright (c) 2009 Michael B. Trausch <mike@trausch.us>
 * License: GNU GPL v3 as published by the Free Software Foundation
 */

public class Program {
	public static int main(string[] args) {
		LibUUID.UUID uuid_time = {};
		LibUUID.UUID uuid_rand = {};

		LibUUID.generate_time(uuid_time);
		LibUUID.generate_random(uuid_rand);

		print_uuid_info(uuid_time);
		print_uuid_info(uuid_rand);

		return(0);
	}

	public static void print_uuid_info(LibUUID.UUID uu) {
		stdout.printf("UUID:\t\t%s\n", helper_uuid_to_string(uu));
		stdout.printf("  Type:\t\t%s\n", helper_uuidtype_to_string(uu));
		stdout.printf("  Variant:\t%s\n",
					  helper_uuidvariant_to_string(uu));

		if(LibUUID.get_type(uu) == LibUUID.Type.TIME) {
			stdout.printf("  Time:\t\t%s\n", helper_uuidtime_to_string(uu));
		}

		stdout.printf("\n");
	}

	public static string helper_uuidtype_to_string(LibUUID.UUID uu) {
		int uuidtype = LibUUID.get_type(uu);

		switch(uuidtype) {
		case LibUUID.Type.TIME:
			return("Time");
		case LibUUID.Type.RANDOM:
			return("Random");
		default:
			return("Unknown, invalid, or not a UUID");
		}
	}

	public static string helper_uuidvariant_to_string(LibUUID.UUID uu) {
		int uuidvariant = LibUUID.get_variant(uu);

		switch(uuidvariant) {
		case LibUUID.Variant.NCS:
			return("NCS");
		case LibUUID.Variant.DCE:
			return("DCE");
		case LibUUID.Variant.MICROSOFT:
			return("Microsoft");
		case LibUUID.Variant.OTHER:
			return("Other");
		default:
			return("Unknown, invalid, or not a UUID");
		}
	}

	public static string helper_uuidtime_to_string(LibUUID.UUID uu) {
		LibUUID.Time uuidTime;
		LibUUID.TimeValue uuidTimeValue;
		uuidTime = LibUUID.get_time(uu, out uuidTimeValue);

		int seconds, microseconds;
		seconds = uuidTimeValue.seconds;
		microseconds = uuidTimeValue.microseconds;
		LibUUID.Time secs = (LibUUID.Time)seconds;

		string uuid_timestamp = LibUUID.get_unixtime_to_string(ref secs);
		GLib.StringBuilder sb = new GLib.StringBuilder();

		sb.append_printf("%s", uuid_timestamp);
		sb.truncate(24);

		sb.append_printf(" (and %d microseconds)", microseconds);

		return(sb.str);
	}

	public static string helper_uuid_to_string(LibUUID.UUID uu) {
		char[37] uu_chars = new char[37];
		LibUUID.unparse(uu, uu_chars);

		GLib.StringBuilder sb = new GLib.StringBuilder();

		foreach(char c in uu_chars) {
			if(c == '\0') break;
			sb.append_printf("%c", c);
		}

		return(sb.str);
	}
}

This program’s output:

Wednesday, 2009-Feb-18 at 14:25:19 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-83/10081-0]:uuid-vala> valac -o uuid uuid-fo.vala --pkg libuuid --vapidir . -X -luuid

Wednesday, 2009-Feb-18 at 14:25:59 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-84/10082-0]:uuid-vala> ./uuid
UUID:		fbf2a006-fdf1-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 14:26:04 2009 (and 691047 microseconds)

UUID:		ae610032-d4bf-42c7-93ff-465dd2e712bb
  Type:		Random
  Variant:	DCE

This program is a bit large for such a simple task, don’t you think? Take a look at the helper functions that are required to make the bindings for libuuid usable. We can certainly do better than this—those helper functions are likely to be needed by just about anyone that works with the libuuid library in Vala. Nearly everyone is going to want to get the UUID as a string, and while the API provides a means of reaching that goal, it doesn’t do it for us, and there is no reason why it shouldn’t. After all, the way it is “just works” in existing C code, and future Vala code should “just work,” too.

The hard part is that the way that the UUID library works is a bit unusual. When you bind something with Vala’s VAPI support, you can create a class out of it, if it gives you a pointer type of some sort. But this doesn’t, and attempting to do so will get the C compiler rather unhappy with you. The answer in this case is to create the VAPI binding, and then create a class that wraps the binding. The reason is that the UUID library doesn’t act very object-like at all, requiring that each UUID that you use be essentially statically allocated; no functions return a new UUID, and there is no way to free them that is consistent with the API. You can certainly dynamically allocate them and work with them, but the UUID library doesn’t seem to think that way.

So, here is a much simplified binding, which we will use as a basis for a wrapper:

/*
 * libuuid.vapi - Vala bindings for the e2fsprogs library libuuid
 * Copyright (c) 2009 Michael B. Trausch <mike@trausch.us>
 * License: GNU LGPL v3 as published by the Free Software Foundation
 *
 * This is a bare simple binding which will be wrapped by a
 * newly-created Vala object.  It's only slightly different than the
 * first version, in that it makes no attempt to make life easier on
 * the programmer using this binding.
 */

[CCode(cheader_filename = "uuid/uuid.h",
	   lower_case_cprefix = "uuid_", cprefix = "UUID_")]
namespace Native.LibUUID {
	// Constants
	private const int VARIANT_NCS;
	private const int VARIANT_DCE;
	private const int VARIANT_MICROSOFT;
	private const int VARIANT_OTHER;
	private const int TYPE_DCE_TIME;
	private const int TYPE_DCE_RANDOM;

	[CCode(cname = "time_t")]
	public struct time_t : int{}

	[CCode(cname = "struct timeval")]
	public struct timeval {
		public int tv_sec;
		public int tv_usec;
	}

	[CCode(cname = "uuid_t")]
	public struct UUID : char {}

	public void clear(UUID uu);
	public void copy(UUID dest, UUID src);
	public void generate(UUID outp);
	public void generate_random(UUID outp);
	public void generate_time(UUID outp);
	public void unparse(UUID uu,
						[CCode(array_length = false,
							   array_nullterminated = false)]
						char[37] outp);
	public void unparse_upper(UUID uu,
							  [CCode(array_length = false,
									 array_nullterminated = false)]
							  char[37] outp);
	public void unparse_lower(UUID uu,
							  [CCode(array_length = false,
									 array_nullterminated = false)]
							  char[37] outp);

	public int compare(UUID uu1, UUID uu2);
	public int is_null(UUID uu);
	public int parse(string uu_in, UUID outp);
	public int type(UUID uu);
	public int variant(UUID uu);

	public time_t time(UUID uu, out timeval TimeInfo);

	[CCode(cname = "ctime_r", cheader_filename = "time.h")]
	public weak string unixtime_to_string(ref time_t secs,
										  [CCode(array_length = false,
												 array_nullterminated = false)]
										  char[] ret);
}

This is (mostly) the same, with a few different choices made in representing information from C. Now, the thing to do is create a wrapper around this; this wrapper will be either compiled into a Vala application or it could be compiled as a shared library, being useful generally to other GLib/GObject programs or other Vala programs.

/*
 * libwrapuuid.vala - Wrapper for the simple libuuid bindings.
 * Copyright (c) 2009 Michael B. Trausch &ltmike@trausch.us>
 * License: GNU LGPL v3 as published by the Free Software Foundation
 *
 * This is a wrapper around libuuid.vapi which presents an object-oriented
 * interface to libuuid.
 */

namespace Trausch.Vala.UUID {
	public enum Variant {
		NCS = Native.LibUUID.VARIANT_NCS,
		DCE = Native.LibUUID.VARIANT_DCE,
		Microsoft = Native.LibUUID.VARIANT_MICROSOFT,
		Other = Native.LibUUID.VARIANT_OTHER
	}

	public enum Type {
		Time = Native.LibUUID.TYPE_DCE_TIME,
		Random = Native.LibUUID.TYPE_DCE_RANDOM
	}

	public errordomain UUIDError {
		MALFORMED_STRING_INPUT,
		NOT_SET,
		WRONG_TYPE,
		FAILED
	}

	public class UUID : GLib.Object {
		private Native.LibUUID.UUID uuid;
		private Type defaultType;

		construct {
			this.defaultType = Type.Time;
			this.generate();
		}

		public static UUID create_from_string(string input) throws UUIDError {
			UUID newUUID = new Trausch.Vala.UUID.UUID();
			newUUID.UUID = input;

			return(newUUID);
		}

		public string? UUID {
			owned get {
				if((bool)Native.LibUUID.is_null(this.uuid)) {
					return(null);
				}

				return(this.to_string());
			}

			set {
				if(value == null) {
					Native.LibUUID.clear(this.uuid);
				}

				if(Native.LibUUID.parse(value, this.uuid) < 0) {
					throw new
						UUIDError.MALFORMED_STRING_INPUT("Malformed string");
				}
			}
		}

		public Type UUIDType {
			get {
				if((bool)Native.LibUUID.is_null(this.uuid))
					throw new UUIDError.NOT_SET("No UUID is currently stored");

				Type uuidType = (Type)Native.LibUUID.type(this.uuid);
				return(uuidType);
			}

			set {
				Type current_type = this.UUIDType;
				Type new_type = value;

				if(current_type != new_type) {
					this.defaultType = new_type;
					this.generate();
				}
			}

			default = Type.Time;
		}

		public Variant UUIDVariant {
			get {
				if((bool)Native.LibUUID.is_null(this.uuid))
					throw new UUIDError.NOT_SET("No UUID is currently stored");

				Variant uuidVariant =
					(Variant)Native.LibUUID.variant(this.uuid);
				return(uuidVariant);
			}
		}

		public void generate() {
			switch(this.defaultType) {
			case Type.Random:
				Native.LibUUID.generate_random(uuid);
				break;
			case Type.Time:
				Native.LibUUID.generate_time(uuid);
				break;
			default:
				Native.LibUUID.generate(uuid);
				break;
			}
		}

		public int compare(UUID that) {
			return(Native.LibUUID.compare(this.uuid, that.uuid));
		}

		public bool equals(UUID that) {
			bool retval = false;
			int compare_result = Native.LibUUID.compare(this.uuid, that.uuid);
			if(compare_result == 0) retval = true;

			return(retval);
		}

		public UUID duplicate() {
			Trausch.Vala.UUID.UUID newUUID = new Trausch.Vala.UUID.UUID();
			newUUID.UUID = this.to_string();

			return(newUUID);
		}

		public string to_string() {
			char[] chars = new char[37];
			Native.LibUUID.unparse(this.uuid, chars);
			GLib.StringBuilder sb = new GLib.StringBuilder();

			foreach(char c in chars) {
				if(c == '\0') break;

				sb.append_printf("%c", c);
			}

			string retval = sb.str;
			return(retval);
		}

		public void from_string(string input) throws UUIDError {
			this.UUID = input;
		}

		private Native.LibUUID.timeval get_raw_time() throws UUIDError {
			if(this.UUIDType != Type.Time)
				throw new UUIDError.WRONG_TYPE("Invalid op: not a time UUID");

			if((bool)Native.LibUUID.is_null(this.uuid))
				throw new UUIDError.NOT_SET("No UUID is currently stored");

			Native.LibUUID.timeval time;
			Native.LibUUID.time(this.uuid, out time);

			return(time);
		}

		/*
		 * This is a slight bit ugly, to format the time the way I wanted to
		 * do it.
		 */
		public string get_time() throws UUIDError {
			Native.LibUUID.timeval timestamp = this.get_raw_time();
			Native.LibUUID.time_t unixTime =
				(Native.LibUUID.time_t)timestamp.tv_sec;

			GLib.StringBuilder sb = new GLib.StringBuilder();
			char[] timeStr = new char[27];
			Native.LibUUID.unixtime_to_string(ref unixTime, timeStr);

			int spaces = 0;

			foreach(char c in timeStr) {
				if(c == '\n') break;
				if(c == ' ') spaces++;

				if(spaces == 4) {
					sb.append_printf("+%dus", timestamp.tv_usec);
					spaces++;
				}

				sb.append_printf("%c", c);
			}

			return(sb.str);
		}

		public string get_type_as_string() throws UUIDError {
			Type uuidType = this.UUIDType;
			string retval = "";

			switch(uuidType) {
			case Type.Time:
				retval = "Time";
				break;
			case Type.Random:
				retval = "Random";
				break;
			}

			return(retval);
		}

		public string get_variant_as_string() throws UUIDError {
			Variant uuidVariant = this.UUIDVariant;
			string retval = "";

			switch(uuidVariant) {
			case Variant.NCS:
				retval = "NCS";
				break;
			case Variant.DCE:
				retval = "DCE";
				break;
			case Variant.Microsoft:
				retval = "Microsoft";
				break;
			case Variant.Other:
				retval = "Other";
				break;
			}

			return(retval);
		}
	}
}

Alright, so that's 234 lines of object-oriented goodness. What does that get us, though? The ability to focus more closely on an actual application! Here is a simple UUID generator program which uses this wrapping. This application is considerably more featureful than the last one with the last bindings without the wrapper class, as can be seen by looking at it:

/*
 * uuid.vala - UUID generator program using libwrapuuid.vala and libuuid from
 * e2fsprogs.
 * Copyright (c) 2009 Michael B. Trausch <mike@trausch.us>
 * License: GNU GPL v3 as published by the Free Software Foundation
 */
using Trausch.Vala.UUID;

public class Program {
	public static bool cmdline_force_time;
	public static bool cmdline_force_random;
	public static bool cmdline_version;

	const OptionEntry[] options = {
		{ "force-time", 't', 0, OptionArg.NONE, ref cmdline_force_time,
		  "Force time-based UUIDs", null },
		{ "force-random", 'r', 0, OptionArg.NONE, ref cmdline_force_random,
		  "Force random UUIDs", null },
		{ "version", 'v', 0, OptionArg.NONE, ref cmdline_version,
		  "Show the program version", null },
		{ null }
	};

	public static int main(string[] args) {
		int howMany = 2;

		try {
			GLib.OptionContext ctx = new GLib.OptionContext("- display uuids");
			ctx.set_help_enabled(true);
			ctx.add_main_entries(options, null);
			ctx.parse(ref args);
		} catch(OptionError e) {
			err(e.message, args[0]);
			return(1);
		}

		if(cmdline_version == true) {
			show_version();
		}

		if(cmdline_force_time && cmdline_force_random) {
			err("--force-time and --force-random are mutually exclusive",
				args[0]);
			return(1);
		}

		if(args.length > 1)
			howMany = (args[1].to_int() == 0 ? howMany : args[1].to_int());

		UUID[] uuids = generate_uuids(howMany);

		foreach(UUID u in uuids) {
			print_uuid_info(u);
		}

		return(0);
	}

	public static void err(string errMessage, string prog) {
		stdout.printf("%s: %s\n", prog, errMessage);
		stdout.printf("Run '%s --help' to see a full list of command line"+
					  " options.\n", prog);
	}

	public static void show_version() {
		stdout.printf("""Sample UUID Generator v0.1.2
Copyright © 2009 Michael B. Trausch <mike @trausch.us>
Licensed under the terms of the GNU GPL v3 as published by the FSF.

""");
	}

	public static UUID[] generate_uuids(int howMany) {
		UUID[] retval = new UUID[howMany];
		for(int cur = 0; cur < howMany; cur++) {
			retval[cur] = new UUID();
			Trausch.Vala.UUID.Type newType;

			if(cmdline_force_time == true) {
				newType = Trausch.Vala.UUID.Type.Time;
			} else if(cmdline_force_random == true) {
				newType = Trausch.Vala.UUID.Type.Random;
			} else {
				if(cur % 2 != 0) {
					newType = Trausch.Vala.UUID.Type.Random;
				} else {
					newType = Trausch.Vala.UUID.Type.Time;
				}
			}

			retval[cur].UUIDType = newType;
		}

		return(retval);
	}

	public static void print_uuid_info(UUID uuid) {
		GLib.StringBuilder sb = new GLib.StringBuilder();
		sb.append_printf("UUID:\t\t%s\n", uuid.to_string());
		sb.append_printf("  Type:\t\t%s\n", uuid.get_type_as_string());
		sb.append_printf("  Variant:\t%s\n", uuid.get_variant_as_string());
		if(uuid.UUIDType == Trausch.Vala.UUID.Type.Time)
			sb.append_printf("  Time:\t\t%s\n", uuid.get_time());
		stdout.printf("%s\n", sb.str);
	}
}

One minor note: You will need Vala's trunk revision from the Subversion or bzr repositories for Vala to build this as is (or wait for a version of Vala >= 0.5.4 to be released). To use Vala 0.5.3, remove the parenthesis on line 94—or the program will try to allocate a ton of memory (trust me, the allocation will fail). It is a relatively new language/compiler, so all the kinks haven't yet been worked out. That's not a big deal, though; as I mentioned earlier, Vala development appears to be very active. When I reported the bug earlier today, the Vala people were very fast to fix the bug (it was fixed in under 30 minutes!) and commit the fix to the SVN repository. I was very pleasantly surprised with that.

The latter version of the binding, the wrapper class, and the Vala source for the application itself should be pretty easy to mentally follow if you're used to reading things like C#, C++, or Java, though if any of it needs additional explanation I can certainly respond to requests. Writing it isn't terribly much harder, though the one bug I found was a bit of a kink since the parenthesis removal wasn't any of the things I thought to check. In any event, you should now know enough to get started with Vala. Oh, and if you don't like systems without documentation, and you're good with documentation, you may contribute documentation resources to the Vala project, if you can at all; Vala is currently short on documentation!

Oh, and of course, a few runs of the new version:

Wednesday, 2009-Feb-18 at 18:05:39 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-189/10177-0]:uuid-vala> ./uuid --help
Usage:
  uuid [OPTION...] - display uuids

Help Options:
  -?, --help             Show help options

Application Options:
  -t, --force-time       Force time-based UUIDs
  -r, --force-random     Force random UUIDs
  -v, --version          Show the program version

Wednesday, 2009-Feb-18 at 18:05:44 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-190/10178-0]:uuid-vala> ./uuid -v -t 1
Sample UUID Generator v0.1.2
Copyright © 2009 Michael B. Trausch <mike@trausch.us>
Licensed under the terms of the GNU GPL v3 as published by the FSF.

UUID:		ad990d72-fe10-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 18:05:47+640357us 2009

Wednesday, 2009-Feb-18 at 18:05:47 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-191/10179-0]:uuid-vala> ./uuid -r 1
UUID:		a57c6345-df45-4417-9298-e1c07d2151a8
  Type:		Random
  Variant:	DCE

Wednesday, 2009-Feb-18 at 18:05:53 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-192/10180-0]:uuid-vala> ./uuid 3
UUID:		b26b2c40-fe10-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 18:05:55+728288us 2009

UUID:		5a431431-cc7c-44bf-99ee-8f967e724b17
  Type:		Random
  Variant:	DCE

UUID:		b26b2c42-fe10-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 18:05:55+728288us 2009

Wednesday, 2009-Feb-18 at 18:05:55 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-193/10181-0]:uuid-vala> ./uuid -t 3
UUID:		b402ee9e-fe10-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 18:05:58+400579us 2009

UUID:		b402ee9f-fe10-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 18:05:58+400579us 2009

UUID:		b402eea0-fe10-11dd-859f-5ae5840a05cf
  Type:		Time
  Variant:	DCE
  Time:		Wed Feb 18 18:05:58+400579us 2009

Wednesday, 2009-Feb-18 at 18:05:58 - mbt@zest - Linux v2.6.29-rc5
Ubuntu Intrepid:[1-194/10182-0]:uuid-vala> ./uuid -r 3
UUID:		275ca880-5ae7-44b3-bcaf-4d0084d00df3
  Type:		Random
  Variant:	DCE

UUID:		d270e2af-5ee0-456d-8ac6-e1d5685287d0
  Type:		Random
  Variant:	DCE

UUID:		9a0de010-aba3-4276-99e9-dcb6e28455f2
  Type:		Random
  Variant:	DCE

ETA: If you would like to download the final sources, they are online in a simple tarball.

4 Comments

  1. Faheem  27th July 2009  

    Only one word I can use to describe this post: Awesome. Documentation for Vala can be somewhat lacking at times but you’ve explained this really well.

    Thank you!

  2. Michael Trausch  27th July 2009  

    You are quite welcome. There is a good amount of source code out there these days written in Vala, and more every day; you may want to look at the Vala home page where there is a list of applications written (partially or completely) in it. AllTray is one of those (which is written mostly in Vala, with a little bit of C glue).

  3. G.S.Alex  2nd January 2010  

    There seems to have a error:

    public void unparse(UUID uu,
    [CCode(array_length = false,
    array_nullterminated = false)]
    char[37] outp);
    should ‘ char[37] outp ‘ be ‘ char[] outp ‘ ?
    I got a error in Vala 0.7.9

  4. Michael Trausch  18th January 2010  

    G.S. Alex: This example is not likely to work in very new versions of Vala. I shall post an update for very current versions of Vala soon.

Leave a Reply

Powered By Wordpress || Designed By Ridgey