Here are some hurdles that I overcame when coding a Ruby Extension in
C++ on
Win32. Feel free to add to this or to comment. I hope it will help
someone
someday. (If you ask WHY?, my answer is: I’m very comfy with STL).
-
For Win32, mkmf generates makefiles for Visual Studio. In other
words,
“make” won’t work for you on Win32, you have to type “nmake”. I used
VC6.
And to use C++, the files you compile must be named “.cpp”, not “.c” so
that
they are compiled as c++ files instead of c files. Perhaps .cc extension
works, I didn’t try.
-
Your static functions cannot be passed down to rb_define_method,
rb_define_global_function etc. The solution is not obvious. My first
thought, extern “C”, __cdecl/__stdcall etc, does not do anything to
solve
it. You have to do the following (found somewhere on net):
// first a typedef
typedef VALUE (*HOOK)(…);
// then do a reinterpret_cast:
rb_define_method(rb_cYadaYada, “initialize”,
reinterpret_cast(yadayada_initialize), 2);
- Destructors in the functions are not called if ruby long jumps out!
This
happens when you raise an exception. Here is an example using the
classical
Lock-A-Mutex when the object is created, and Unlock-A-Mutex when the
destructor is called:
Lock my_lock(mutex);
connected = rb_iv_get(self, “@connected”);
if(connected != Qtrue)
{
rb_raise(rb_eRuntimeError, YadaYada is not connected");
}
When the exception is raised, the destructor of my_lock WILL NOT BE
CALLED
so the Mutex is not unlocked. Nasty!
- You have to #include before <ruby.h>. Otherwise you will get
a
lot of compile errors when #include:ing . This might apply to
other
include files, but it seemed not to apply to and .
Cheers/D
On Mon, 28 Jan 2008 02:50:39 -0500, daniel åkerud wrote:
not “.c” so that they are compiled as c++ files instead of c files.
rb_define_method(rb_cYadaYada, “initialize”,
other include files, but it seemed not to apply to and .
Cheers/D
Try writing the whole OO interface in C++, and letting SWIG do the
mapping into ruby for you. SWIG knows (with appropriate includes) about
std::vector (which maps to Array) and std::string (which maps to
String).
–Ken
On Mon, Jan 28, 2008 at 04:50:39PM +0900, daniel ?kerud wrote:
// first a typedef
typedef VALUE (*HOOK)(…);
// then do a reinterpret_cast:
rb_define_method(rb_cYadaYada, “initialize”,
reinterpret_cast(yadayada_initialize), 2);
The problem is not that the methods are static; it’s that they don’t
have the same signature as the rb_define_method function expects. You
can cast to the right type using the RUBY_METHOD_FUNC() macro (this is
what Rice does - see http://rice.rubyforge.org - though it doesn’t work
with a compiler as old as VC6).
Some compilers also check the calling convention (e.g. comeau).
Unfortunately, C++ provides no mechanism to specify both the calling
convention and the linkage, so if your compiler uses a different
calling convention for C and C++ code, you have to make your wrapper
functions extern “C” (which is a nuisance, since it means it’s
impossible to have internal linkage and C calling convention on such
compilers).
- Destructors in the functions are not called if ruby long jumps out! This
happens when you raise an exception.
IIRC the C++ standard does not specify what happens if you longjmp over
code with objects on the stack that have destructors with non-trivial
behavior. In other words, your destructor might get called, or it might
not.
The solution here is to use rb_protect whenever you call into Ruby code.
This is what Rice does under the hood.
There’s a similar problem with allowing C++ exceptions to escape into
Ruby-land; on most compilers I’ve used, this causes std::terminate to
get called.
- You have to #include before <ruby.h>. Otherwise you will get a
lot of compile errors when #include:ing . This might apply to other
include files, but it seemed not to apply to and .
I’ve not experienced this problem. What kinds of errors do you get?
Paul