Embedding ruby (*not* extending)

I need to embed a scripting languange in a C++ application.

Currently I am evaluating some scripting languages for this purpose.

  • perl (that works, although it needs a big amount of C++ glue code
    to deal with the perl stack(s)
  • python - have not tried yet
  • lua - that’s really great, very easy to embed. Not surpring since
    lua is “designed to be embedded”. Unfortuneately the lua interpreter is
    very limited
  • ruby - there is no/very limited docs about howto do that

to clarify: I am not talking about extending ruby, e.g. via SWIG. That
is quite easy to do.

What I mean is embedding it into an existing large C++ program and
than call some ruby functions (and to give some values to this ruby
functions and to get some values back from this ruby functions)

As it is in the following lua example:

 int main (void) {
   lua_State *L = lua_open();

   /* load and parse script - but do not execute */
   luaL_loadfile(L, "example.lua");

   /* push functions and arguments */
   lua_getglobal(L, "f");  /* function to be called */
   lua_pushnumber(L, x);   /* push 1st argument */
   lua_pushnumber(L, y);   /* push 2nd argument */

   /* do the call (2 arguments, 1 result) */
   lua_pcall(L, 2, 1, 0);

   /* retrieve result */
   int result = lua_tonumber(L, -1);
   lua_pop(L, 1);

   lua_close(L);
   return 0;
 }

I have found some docs a la:

int main(int argc, char **argv)
{
     RUBY_INIT_STACK
     ruby_init();
     ruby_options(argc, argv);
     ruby_run();

return(0);
}

But I did not find a simple example describing howto bring values onto
the ruby stack, call a function and get results back. Moreover I found
out that ruby_run() does not return. I need to have the C++ application
keeping control.

On Thursday 27 March 2008, John S. wrote:

  • ruby - there is no/very limited docs about howto do that

return(0);
}

But I did not find a simple example describing howto bring values onto
the ruby stack, call a function and get results back. Moreover I found
out that ruby_run() does not return. I need to have the C++ application
keeping control.

Embedding ruby is not too difficult, even if, as you noticed, there
isn’t much
documentation. Essential documentation is the Ruby C API, which you can
find
at http://www.ruby-doc.org/doxygen/1.8.4/index.html (look expecially at
the
Global page). Unfortunately, it only lists functions and structures, but
doesn’t explain them. A little explanation of the most useful functions
is
given in the chapter “Extending ruby” of the pickaxe.

As first thing, you should remove ruby_run, since it works more or less
like
Kernel#exec, which is not what you want. To load a file (notice that you
can’t
load but not execute a file in ruby) you can use rb_require or
rb_f_load,
which work exactly like require and load in ruby. Calling a method is
conceptually easy: you simply use the rb_funcall function, passing it
the
receiver of the method, an int corresponding to the name of the method
(usually obtained using the rb_intern function), the number of
arguments, and
the arguments themselves.

The tricky part is to obtain the receiver of the method. If the method
is a
global method, you can obtain the receiver with:

VALUE rec = rb_eval(“main”);

You can create an instance of a class with the following code:
rb_funcall(rb_const_get(rb_mKernel, rb_intern(“Cls”)), rb_intern(“new”),
n_args, arg1, arg2,…);
where n_args is the number of arguments you pass and arg1, arg2 and so
on are
the actual arguments.

On this mailing list, I recently saw mentioned a project, called “rice”
which
should be a C++ interface to the ruby C API. I never used it, but you
may find
it useful. It’s at http://rubyforge.org/projects/rice.

I hope this helps

Stefano

John S. wrote:

  • ruby - there is no/very limited docs about howto do that
    There is just enough information on the first version of PickAxe to get
    you started. Which compiler are you using?

Cheers,
Mohit.
3/27/2008 | 5:15 PM.

Mohit S. wrote:

is very limited

  • ruby - there is no/very limited docs about howto do that

There is just enough information on the first version of PickAxe to
get you started. Which compiler are you using?

I should have added that I collected a few of the main links related to
this on my blog post when I started to embed Ruby into a Borland C++
Builder application:
http://notepad.onghu.com/2008/1/2/codegear-turbo-c-and-ruby

Cheers
Mohit.

There is just enough information on the first version of PickAxe to
get you started.

I think I disagree on this aspect. Getting started probably means very
sparse documentation, I just hope that people that are knowledgable
about it and did so, write a little bit about it. I even found some very
good blogs or entries therein explaining not-so-often-used tricks in the
Ruby-C land. :wink:

I’ll expound on Stephan’s post about Rice.

Embedding Ruby with Rice is very easy:

int main(int argc, char * argv[])
{
  Rice::VM vm(argc, argv);
  vm.run()
}

You’ve now got access to the Ruby VM in your application. You can use
all of the normal Ruby C API calls, or you can use Rice’s C++ API to
get a hold of Ruby information. A couple of examples (not tested, may
not be exactly correct):

Calling your global (Kernel) method fact():

Ruby:

def fact(a, b)
    ...
end

C++:

int result = Module(rb_mKernel).call("fact", 1, 2);

Or to get a hold of a class defined in your Ruby code

Ruby:

class MyClass
    ....
 end

C++

// There's a better way to do this, I can't find it right now
Rice:Class myClass =

Class(Module(rb_mKernel).const_get(“MyClass”).call(“new”));
myClass.call(“myFunc”);

And when you need to expose C++ classes into Ruby, that’s trivially
easy (you’ve probably looked at luabind, it’s similar):

define_class(“MyClass”)
.define_method(“method_name”, &ClassType::method);

and in your Ruby you can now:

m = MyClass.new
m.method_name

Hope that helps. If you give this library a try any questions you have
can be sent to me, Paul B. ([email protected]) or join
#ruby-rice on freenode.

Jason R.

From: “John S.” [email protected]

BTW: I am developing inside a cross-platform application. It uses:

  • Microsoft Visual Studion 2008
  • gcc - at least v3.3.3 (for Linux and MacOS)

Hi,

We embed ruby into a cross-platform application, as well.
(MSVC 2003 on Windows, gcc 4.0.1 on OS X, at the moment.)

The embedding code we use today is still very similar to what
I posted a couple years ago here:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/178389

Looks like the main difference now is that we now longer
call NtInitialize (our own program startup has already taken
care of that stuff.)

We also ship a subset of the ruby standard library with our
app, as we don’t want to depend on any particular version
of ruby being preinstalled on the system. So we set up some
include paths for ruby to point to the application-local
stdlib. For ex:

#ifdef PLATFORM_WINDOWS
inc_path = PathOps::concat(application_path, “ruby/lib/ruby/1.8”);
ruby_incpush(inc_path.c_str());
inc_path = PathOps::concat(application_path,
“ruby/lib/ruby/1.8/i386-mswin32”);
ruby_incpush(inc_path.c_str());
#endif
#ifdef PLATFORM_MACOSX
inc_path = PathOps::concat(application_path, “ruby/lib/ruby/1.8”);
ruby_incpush(inc_path.c_str());
inc_path = PathOps::concat(application_path,
“ruby/lib/ruby/1.8/powerpc-darwin8.3.0”);
ruby_incpush(inc_path.c_str());
#endif

Architecturally, we run ruby (1.8.4) on its own native thread,
and communicate between the ruby thread and native application
threads via a bridge (using boost::recursive_mutex and
boost::condition.) (Actually the ruby side doesn’t use
boost::condition, but polls at about 10Hz. When we switch to
ruby 1.9, we’ll be able to eliminate the polling on the ruby
side.)

Be careful if you run ruby on a different thread than the
main thread, as the default pthread stack size for threads
other than the main thread is very small. We modified the
boost thread library to specify a much larger stack size
when creating threads on OS X.

Hope this helps,

Bill

John S. wrote:

  • ruby - there is no/very limited docs about howto do that

to clarify: I am not talking about extending ruby, e.g. via SWIG. That
is quite easy to do.

What I mean is embedding it into an existing large C++ program and
than call some ruby functions (and to give some values to this ruby
functions and to get some values back from this ruby functions)
[…]

thanks a lot guys. Your responses where very very helpful!

I’m going to dive into all of this interesting resources.

BTW: I am developing inside a cross-platform application. It uses:

  • Microsoft Visual Studion 2008
  • gcc - at least v3.3.3 (for Linux and MacOS)