embedding ruby (*not* extending)

J

John Smith

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.
 
S

Stefano Crocco

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.

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
 
M

Mohit Sindhwani

John said:
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

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.
 
M

Marc Heiler

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. ;-)
 
J

Jason Roelofs

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<ClassType>("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 Brannan ([email protected]) or join
#ruby-rice on freenode.

Jason R.
 
J

John Smith

John said:
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)
[...]

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)
 
B

Bill Kelly

From: "John Smith said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top