Virtualising C in Ruby

As I’ve now left Gentoo, one of my major projects is Rust, that if I can actually complete will be a nice piece of software for ruby developers out there, as it will allow to write Ruby extensions based on C libraries without the need to write all the boilerplate code that can easily be copy pasted wrong, or that, if flawed, would need to be fixed for N instance of the same piece of code.

As I said before, Rust is not yet to the same level of C++ bindings generation as my previous generator, but I’m still quite satisfied with what it’s coming to be because the code is way cleaner that the generator, and way more flexible.

One of the features that my previous generator had and Rust didn’t up to tonight were the «virtual variables». What is a virtual variable? Well, when you write a C++ class, you don’t usually make variables public, or the user (of the library, not the final user of course) can change these to values that are not supposed to be set, and does not allow the code of the class to take changes into account; for this reason the usual method to allow the class’s users to actually change the values of the internal variables you use the so-called getter and setter methods, usually named foo and setFoo or getFoo and setFoo (where foo is the actual name of the variable with a recognisable meaning, and m_foo – or variation thereof – is the name of the private variable). You can do the same in Ruby of course, but there is also the option of naming the methods foo and foo=, and then use the variable foo as a generic variable for the instance, both getter and setter methods will be called automatically.

What a virtual variable do in Rust, is wrapping getters and setters so that they are visible as a variable, with foo and foo= as Ruby function names (and at the same time leave the true name of the methods available for library compatibility). It’s a nice way to wrap around a lot of getter/setter methods pair, and RubyTag++ had quite a lot of them.

Even on the previous generator, the setter and getter pairs were simply expanded in functions, but with the new architecture, this is made even simpler, as I just have to create the two method objects, and then yield them so that the user (of Rust) can provide the extra support needed, like providing the presence of an instance parameter for the C class wrappers (that now I think I should have called virtual classes).

Now, the other feature that is needed for RubyTag++ (although I’m not yet sure if that is the last feature) is an easy way to convert from Ruby integers (be them literals or constants) to C/C++ enum values. While the generator didn’t really carry the values of the enum together with the enum itself, I’ve been now thinking that it would be nice to check if the value coming from Ruby is actually in the list of supported values for the enum, and throw an exception otherwise, instead of passing a maybe incompatible parameter to the C/C++ code.

From this I also considered another useful thing to do with enums: virtual enums. Basically, many C libraries provide some list of #define constants that are progressive, and are used to indicate one out of a series of possible values for a function parameter (for instance). Xine does that, for visual types. I can leave the parameter for the xine_open_video_port() function to accept an integer, and then hope that xine-lib handles it properly, or I can make Rust smart and create a virtual enum that would check for the value sent by the Ruby layer, and again throw an exception if an incompatible parameter is passed.

And again, I’ve been thinking a way to handle the usual situation where a function returns 0 for a success and then a positive or negative value with the error number, or when they return 1 for success, 0 for failure and require calling another function to get the errno (this method is often used in PulseAudio, Avahi and other libraries too). What I’m thinking here is doing sorta like Ruby itself does with syscall errors (Errno:: module) and provide virtual exceptions, converting the integer errno values in proper Ruby exceptions.

What I’m trying to do now is to make Rust be not just a way to produce the C code needed to bind 1:1 the C++ classes or C function sets into Ruby, but also a way to produce an extension that uses Ruby coding standards, as much as possible (this is also why camelCase names for methods are replaced by underlined_names, like QtRuby/Korundum do already).

I’ll try to write more in the next days about the limits of Rust (there are some obvious and some less obvious), and why I’m writing it using C++ features; hopefully by dumping here my thoughts, I’ll be able to extract some documentation to ship with Rust itself.