Working with Vala for over two years now, I’ve come to admire more and more of its language features. Some of them were quite surprising when I discovered them, making me wonder why exactly they were implemented this way or that. Once I dug through the learning curve, I came to understand and appreciate their power. One of those language features is enum.
Enumerated types, of course, have been around for quite a while. In many languages they’re little more than auto-generated constant int’s (<cough> C </cough>) while in others they have more type safety and correctness surrounding them (such as Java’s approach, which takes enums to the extreme). Vala takes an interesting middle ground in this field.
On one hand, enums in Vala are much like C’s enums; they can be treated as ints, plain as day, and convert back and forth with amazing ease:
enum Suit { SPADE, HEART, DIAMOND, CLUB } void main() { Suit suit = Suit.SPADE; int i = suit; stdout.printf("suit=%d i=%dn", suit, i); }
Personally, I’d prefer it if at least a cast was required, but that’s a nit.
What’s useful about enums is that Vala provides an automatic to_string() method for them, just like int, but with a twist:
stdout.printf("suit=%s i=%sn", suit.to_string(), i.to_string());
Produces:
suit=SUIT_SPADE i=0
Very handy for debugging! Unfortunately, SUIT_SPADE is not such a great text label for your soon-to-be-famous GTK+ War! card game. It turns out you can override the to_string() method by providing your own:
enum Suit { SPADE, HEART, DIAMOND, CLUB; public string to_string() { switch (this) { case SPADE: return "Spade"; case HEART: return "Heart"; case DIAMOND: return "Diamond"; case CLUB: return "Club"; default: assert_not_reached(); } } }
Note some important things here:
- The semicolon must be used to end the list of enumerated values;
- although you’re “overriding” Vala’s built-in to_string() method, you don’t use the override keyword (unlike Java, enums are not a class nor derived from one);
- use assert_not_reached() (or some debugging/logging action) in the default case, as Vala allows for transparent casting of bare ints to enums.
That said, this is pretty slick now. enum now looks more like it’s object-oriented brethren than a third cousin twice-removed. And Vala goes all the way with this, allowing any number of methods (member and static) be added to an enumeration:
enum Suit { SPADE, HEART, DIAMOND, CLUB; public string to_string() { switch (this) { case SPADE: return "Spade"; case HEART: return "Heart"; case DIAMOND: return "Diamond"; case CLUB: return "Club"; default: assert_not_reached(); } } public bool is_higher_than(Suit suit) { return this < suit; } public static Suit[] all() { return { SPADE, HEART, DIAMOND, CLUB }; } } void main() { stdout.printf("%s > %s? %s.n", Suit.SPADE.to_string(), Suit.HEART.to_string(), Suit.SPADE.is_higher_than(Suit.HEART).to_string()); foreach (Suit suit in Suit.all()) stdout.printf("%sn", suit.to_string()); }
Produces:
Spade > Heart? true. Spade Heart Diamond Club
By riding this middle ground (enums really just ints under the covers, but extensible with methods) Vala offers some of the power and flexibility that a Java-like language has while still producing C code compatible with other languages (useful when building a library). In fact, the automatic to_string() method works even with enums from non-Vala libraries (i.e. GLib, GTK, etc.); valac generates the function for you (that is, the library doesn’t have to supply one, although if it does, you can patch it in via its VAPI).
The above code sample points to the other item I would throw on the enum wishlist: either an automatic method (like to_string()) that returns an array of all enumerated values, or simply make the type name iterable:
foreach (Suit suit in Suit) stdout.printf("%sn", suit.to_string());
I realize this looks wrong in so many ways, but so does switch(this) when you first encounter it. Whatever the syntax, iterating over all the values of an enumerated type is a common task, and manually maintaining an array (as in the above example) is error-prone.
Addendum: Eric ran into a bug with enums and to_string() that’s worth mentioning: if you call an implicit (i.e. auto-generated) to_string() from within an enum method, you currently have to use this, that is, this.to_string(). The bug is reported here.
Update: There is an outstanding Vala ticket for allowing foreach to iterate over all enums. A patch was recently submitted, so it’s possible this will be coming soon.
I have started to study Vala recently and your article is very helpful. Thanks!
Hmm. So they’re really not typesafe? That’s kind of yucky…
As for iterating, how about
foreach (Suit s)
stdout.printf(“%sn”, s.to_string());
IMHO that would be syntactic sugar at its sweetest.
Nicer way to get enum as string:
public string get_name() {
return this.to_string().replace(“__”, “”);
}
Hmm, tags removed some parts of my post. that should be:
public string get_name() {
return this.to_string().replace(“CLASSNAME_ENUMNAME_”, “”);
}
Or another way without any hard coded strings:
public string get_name() {
return this.value_nick;
EnumClass enumc = (EnumClass) typeof (ENUMNAME).class_ref ();
unowned EnumValue? eval = enumc.get_value (this);
return_val_if_fail (eval != null, null);
return eval.value_nick;
}
Interesting, these kind of details in Vala realy brings C to a higher level of programming. =)