Running Ruby in C
Embedding Ruby requires one header
ruby.h, which includes a platform-specific
ruby/config.h. You will probably need to tell your compiler about the
include paths for these headers. You will also need to link with the Ruby lib.
On my machine, my minimal compiler options are
Though, if available, you should really use
pkg-config to get the appropriate
options for your distribution:
Including the Ruby interpreter in your C/C++ program is pretty simple. Just
include the header, call a startup function in
main before you use the API,
and a cleanup function after you’re done:
If the VM fails to start during
ruby_init() it will print an error and exit
your program! If you would rather have a softer error, you can instead call
ruby_setup() which returns a nonzero value if a failure occurred
(unfortunately it is not clear how to get a message for the error1).
If an error occurs during
rb_cleanup(), it returns a nonzero
value—otherwise it returns the argument you passed it. This allows a
little shortcut for returning an error status if the cleanup fails (as
demonstrated in the previous example).
Technically you don’t have to call
main, but the
Ruby VM assumes that all future Ruby code will be run from the same stack frame
or a lower one (for garbage collection purposes). The easiest way to ensure this
is to do set up at the top-level of your program, though other approaches could
work. But it would be a bad idea, for example, to init Ruby in some
deeply-nested function, pop a bunch of stack frames, and then run a bunch of
During cleanup, the VM might evaluate more Ruby code (if you passed a block to
at_exit, for example) which could raise an exception.
these by returning a nonzero value and printing an error message. If you instead
ruby_finalize() they will be raised normally (see the section on
Exceptions for how to handle them).
Here’s an alternative example:
Other than the stack frame warning above, there is another limitation: you only
get one Ruby VM per process. The startup/teardown might make it look like you
can keep on destroying and rebuilding the VM over and over again, but
ruby_cleanup only makes sure that your Ruby code is all cleaned up and done.
It doesn’t fully clean up the VM state such that it is ready to be
re-initialized: if you call
ruby_init again, it will fail.
If for some reason you need multiple Ruby VMs in your program, you will need to spin them off in multiple processes to bypass this limitation.
Tweaking the VM
You now have a bare-bones Ruby VM running, but you may want to set up a little
more stuff before you start running Ruby code. To set the name of the Ruby
$0) for error messages and such, use
To set up the load path so that gems can be loaded with
You can also pass options to the VM just like you would to
ruby on the command
line. This is handy for stuff like setting the warning level or verbose
The arguments to
argv just like a main function.
And just like the main of the
ruby program, the VM expects to get some Ruby
code when you call it. If you don’t give it the filename of a script to load or
code to run with
-e, it will try to read from
stdin. If you want to set
options but not run any Ruby code, you can pass it an empty line:
ruby_options() returns a “node” that represents the compiled Ruby code. In some
cases (such as a syntax error) the node will be invalid and you shouldn’t run
ruby_executable_node() checks for this. If the node is valid, you can run it
ruby_exec_node(). The state returned by
the pointer) and by
ruby_exec_node() will be nonzero if an exception was
raised while compiling or running the code. You can read the exception
yourself, or just pass
ruby_cleanup() and it will
print an appropriate error message.
Ruby currently doesn’t support any other way of compiling and running code separately3.
Now you’re ready to interact with Ruby! Go back to the C API.
error_print()to get an error message, but this function isn’t exposed to the API. Is this a normal exception? ↩
In my tests I couldn’t get flags like
-vto do anything. This could be related to
ruby_prog_init(). And really it should be possible to do this without parsing command line options. ↩
It looks like the function
rb_load_file()should do this, but I haven’t had any luck getting it to work. ↩