Having worked with Lua before and made a LuaCppInterface, I decided about a year ago to start working on a C++ interface for mounting mruby as a scripting language.
To start, I have been developing in Ruby for several years now and I found it to be quite a pleasant language to work with. The particularly attractive thing about Ruby is its ability to create very clean DSLs. I have proceeded to use this to build some DSLs of my own.
Despite the power of Ruby, it turned out to be incredibly hard to use as scripting language. However, recently a Japanese-Government-funded project mruby has been taking off and has headed into version 2.0.1. A year ago when I tried using it it was in version 1.4.0, and it was already in a fairly stable state. Since its syntax is meant to be compatible with Ruby 1.9 and its easy to compile into a C++ project, I decided to try it out.
To that end, I created mruby-cpp which provides a C++ frontend into mruby. Its still in beta as I trawl through the mruby C API and slowly gain an understanding of it. I’ll continue to evolve it until it makes sense in both the C++ and mruby contexts.
To start, mruby-cpp is a header-only C++ library. Just clone it into your sources as a submodule or copy it in and start using it. Your executable obviously needs to be linked with
You can run scripts like this:
1 2 3 4 5 6 7
You can also set and get global variables, instance variables and class variables:
1 2 3 4 5 6 7 8
You can bind your C++ classes and their methods to mruby, and specify constructor arguments too!
1 2 3 4 5 6 7 8 9 10
Hopefully you will have a good idea of what I plan to achieve with this library. There are more examples in the tests folder.
See the tests for more examples!
While I was writing this library, I’ve learned a few things about mruby and actually about Ruby itself too.
The C API isn’t well documented
One of the first things I realize when I started looking for ways to do things in mruby is its C API is sparsely documented. There are a bunch of comments in the headers but they are not nearly detailed enough.
A lot of functions have very abbreviated names such as
1 2 3 4 5 6
I still don’t know what
mrb_vm_special_get mean, but I know
cv means class variable and
const means constant. These are the
vm variations, which means they only access global scope. Strangely enough, matz removed
vm variations for the instance variables. Not sure why that is the case.
Sometimes typenames are confusing and its not entirely clear how some types are converted to other types, such as
Difficulties I faced
While writing mruby-cpp, I encountered some difficulty with unifying mruby objects. All things in mruby are objects but they are not treated that way in the API. This is something I still have to solve.
Contrast with Lua where it exposes its GC reference api allowing you to increment and decrement references to its objects, and allowing you to create your own objects on the Lua GC and reference them the same way. This means that you can basically have a variant of the
shared_pointer but managed by the Lua GC.
In mruby when you create a function you are forced to assign it to a class with a name. However, you can create a
proc but it has a transparent
self keyword, which means its impossible to associate it to an object.
As a result, the easiest way to work with mruby-cpp right now is to create classes and bind functions to them so you can use them in mruby. It makes little to no sense creating a function and assigning it to a variable. I am still working on a nice way to do this, and I spent hours trying to find a way to pass a function to mruby as a callable object.
The differences between procs, objects, classes and modules also mean that it was difficult to create an
mruby::Object class. Meaning, pure ruby objects cannot really be passed to C++ with mruby-cpp right now.