The recent C++/ruby post has reminded me to say a few words about my
travails in ruby C++ extensions, if only to possibly help some poor
soul googling in the future.

First, require 'mkmf';create_makefile("cxxruby") will use
CONFIG['LDSHARED'] to create your extension, which is most likely
wrong.  For gcc this results in the error

undefined symbol: __gxx_personality_v0	

when you attempt to load the module.  The linker must be C++ aware due
to static C++ objects: exceptions, iostream, rtti, etc.  I solved this
by setting CONFIG['LDSHARED'] = "g++ -shared".

This solution is nonportable -- perhaps someone can suggest a better
way?  To be consistent, ruby could detect the presence/absence of a
C++ compiler during ./configure, setting CONFIG['CXX'] and
CONFIG['CXX_LDSHARED'].  Also, mkmf should know to use CXX_LDSHARED
when C++ sources exist.

Second, ruby's exception handling via setjmp/longjmp effectively means
you should never construct a C++ object with a nontrivial destructor
on the stack.  If ruby longjmps out of your code, your destructors
will not be called.

Therefore if a ruby method you are implementing requires the use of
temporary C++ objects, you must construct those objects on the heap.

The easiest and saftest technique I found is to write three C
functions for a ruby method: the actual ruby hook, the body, and the
ensure:

#include "ruby.h"

class CXXRuby { } ;

static VALUE rb_cxxruby_superfunk_body( void** args ) ;
static VALUE rb_cxxruby_superfunk_ensure( void** args ) ;

static VALUE rb_cxxruby_superfunk( int argc,
                                   VALUE* argv,
                                   VALUE self )
{
    // construct all C++ objects on the heap

    CXXRuby* cxx_obj = 0 ;

    void* args[4] ;
    args[0] = reinterpret_cast<void*>(argc) ;
    args[1] = static_cast<void*>(argv) ;
    args[2] = reinterpret_cast<void*>(self) ;
    args[3] = static_cast<void*>(&cxx_obj) ;

    try
    {
        cxx_obj = new CXXRuby() ;
    }
    catch(...)
    {
        rb_cxxruby_superfunk_ensure(args) ;
        rb_raise(rb_eRuntimeError,
                 "caught exception from an unfunky constructor") ;
    }

    return rb_ensure(
        reinterpret_cast<VALUE(*)(...)>(rb_cxxruby_superfunk_body),
        reinterpret_cast<VALUE>(args),
        reinterpret_cast<VALUE(*)(...)>(rb_cxxruby_superfunk_ensure),
        reinterpret_cast<VALUE>(args)) ;
}

static VALUE rb_cxxruby_superfunk_body( void** args )
{
    int argc = reinterpret_cast<int>(args[0]) ;
    VALUE* argv = static_cast<VALUE*>(args[1]) ;
    VALUE self = reinterpret_cast<VALUE>(args[2]) ;
    CXXRuby* cxx_obj = *static_cast<CXXRuby**>(args[3]) ;

    try
    {
        VALUE get_out ;
        VALUE the_funk ;
    
        if( rb_scan_args(argc, argv, "11",
                         &get_out, &the_funk) == 1 )
        {
            the_funk = Qtrue ;
        }

        // ...
    }
    catch(...)
    {
        rb_raise(rb_eRuntimeError, "insufficient funk levels") ;
    }

    return self ;
}

static VALUE rb_cxxruby_superfunk_ensure( void** args )
{
    // C++ cleanup

    CXXRuby* cxx_obj = *static_cast<CXXRuby**>(args[3]) ;
    delete cxx_obj ;
    return Qnil ;
}

static VALUE cCXXRuby ;

extern "C" {
    
void Init_cxxruby()
{
    cCXXRuby = rb_define_class("CXXRuby", rb_cObject) ;
    rb_define_singleton_method(
        cCXXRuby,
        "superfunk", 
        reinterpret_cast<VALUE(*)(...)>(rb_cxxruby_superfunk),
        -1) ;

    rb_define_global_const("OperationSupergroove", cCXXRuby) ;
}
    
} // extern "C"