Coding Ruby Extension with C++ on Win32

Discussion in 'Ruby' started by daniel åkerud, Jan 28, 2008.

  1. [Note: parts of this message were removed to make it a legal post.]

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

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

    2. 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<HOOK>(yadayada_initialize), 2);

    3. 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!

    4. You have to #include <string> before <ruby.h>. Otherwise you will get a
    lot of compile errors when #include:ing <string>. This might apply to other
    include files, but it seemed not to apply to <map> and <list>.

    daniel åkerud, Jan 28, 2008
    1. Advertisements

  2. daniel åkerud

    Ken Bloom Guest

    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 Bloom, Jan 28, 2008
    1. Advertisements

  3. daniel åkerud

    Paul Brannan Guest

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

    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.
    I've not experienced this problem. What kinds of errors do you get?

    Paul Brannan, Feb 21, 2008
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.