Skip to content

Instantly share code, notes, and snippets.

@ammar
Created May 25, 2012 10:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ammar/2787174 to your computer and use it in GitHub Desktop.
Save ammar/2787174 to your computer and use it in GitHub Desktop.
Examples of wrapping rb_funcall and rb_funcall2 with rb_rescue2.
#define MY_FUNCALL_ARGC 3
VALUE my_funcall(VALUE receiver, ID method_id, int argc, ...);
VALUE my_funcall_ex(VALUE data);
VALUE my_funcall2(VALUE receiver, ID method_id, int argc, VALUE *argv);
VALUE my_funcall2_ex(VALUE data);
// my_rescue: exception handler used by both of the wrappers below.
// ----------------------------------------------------------------------------
static VALUE
my_rescue(VALUE data, VALUE exception)
{
// do something...
}
// my_funcall: wraps calls to rb_funcall (or rb_funcall2, depending on argc)
// in a call to rb_rescue2. Catches any decesndant of rb_eException.
// ----------------------------------------------------------------------------
VALUE
my_funcall(VALUE receiver, ID method_id, int argc, ...)
{
va_list arg_list;
long argi;
VALUE *argv;
VALUE result = Qnil;
if( argc > 0 ) {
va_start(arg_list, argc);
argv = ALLOCA_N(VALUE, (argc + MY_FUNCALL_ARGC));
argv[0] = argc;
argv[1] = receiver;
argv[2] = method_id;
for( argi = 0; argi < argc; argi++ ) {
argv[argi + MY_FUNCALL_ARGC] = va_arg(arg_list, VALUE);
}
va_end(arg_list);
} else {
argv = ALLOCA_N(VALUE, MY_FUNCALL_ARGC);
argv[0] = argc;
argv[1] = receiver;
argv[2] = method_id;
}
for( argi = 0; argi < (argc + MY_FUNCALL_ARGC); argi++ ) {
rb_gc_register_address(&argv[argi]);
}
result = rb_rescue2(RUBY_METHOD_FUNC(my_funcall_ex), (VALUE)argv,
RUBY_METHOD_FUNC(my_rescue), (VALUE)argv,
rb_eException, (VALUE) 0);
for( argi = 0; argi < (argc + MY_FUNCALL_ARGC); argi++ ) {
rb_gc_unregister_address(&argv[argi]);
}
return( result );
}
// my_funcall_ex: the actual call to rb_funcall or rb_funcall2 to be made by
// my_funcall.
// ----------------------------------------------------------------------------
static VALUE
my_funcall_ex(VALUE data)
{
VALUE *args = (VALUE*) data;
if( args[0] > 0 ) {
return rb_funcall2(args[1], args[2], args[0], &args[3]);
} else {
return rb_funcall(args[1], args[2], args[0]);
}
}
// ----------------------------------------------------------------------------
// my_funcall: wraps calls to rb_funcall2 in a call to rb_rescue2. Catches any
// decesndant of rb_eException.
// ----------------------------------------------------------------------------
VALUE
my_funcall2(VALUE receiver, ID method_id, int argc, VALUE *argv)
{
int argi = 0;
VALUE args[4];
VALUE result;
args[0] = receiver;
args[1] = method_id;
args[2] = argc;
args[3] = (VALUE)argv;
for( argi = 0; argi <= MY_FUNCALL_ARGC; argi++ ) {
rb_gc_register_address(&args[argi]);
}
result = rb_rescue2(RUBY_METHOD_FUNC(my_funcall2_ex), (VALUE)&args[0],
RUBY_METHOD_FUNC(my_rescue), (VALUE)&args[0],
rb_eException, (VALUE) 0);
for( argi = MY_FUNCALL_ARGC; argi >= 0; argi-- ) {
rb_gc_unregister_address(&args[argi]);
}
return( result );
}
// my_funcall2_ex: the actual call to rb_funcall2 to be made by my_funcall2.
// ----------------------------------------------------------------------------
static VALUE
my_funcall2_ex(VALUE data)
{
VALUE *args = (VALUE*) data;
return rb_funcall2(args[0], args[1], args[2], (VALUE*)args[3]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment