2012-06-18 12 views
6

Chcę zadzwonić kod ruby ​​z mojego własnego kodu C. W przypadku podniesienia wyjątku muszę zabezpieczyć kod ruby, który wywołuję. rb_protect wygląda następująco:jak rb_protect wszystko w ruby ​​

VALUE rb_protect(VALUE (* proc) (VALUE), VALUE data, int * state) 

Więc proc musi być funkcja, która bierze VALUE argumenty i zwraca VALUE. Muszę zadzwonić do wielu funkcji, które nie działają w ten sposób. W jaki sposób mogę je zgłaszać, podnosząc wyjątki?

Myślałem o użyciu Data_Make_Struct, aby zawinąć wszystko w jeden obiekt ruby ​​i wywołać na nim metody. Data_Make_Struct może sam zgłosić wyjątek. Jak mogę rb_protectData_Make_Struct?

Odpowiedz

4

Aby w elastyczny sposób używać funkcji rb_protect (np. Wywoływać funkcję Ruby z dowolną liczbą argumentów), należy przekazać małą funkcję przesyłania do rb_protect. Ruby wymaga, aby sizeof(VALUE) == sizeof(void*) i rb_protect ślepo przekazywał dane o wartości VALUE do funkcji wysyłki bez sprawdzania ani modyfikowania tego. Oznacza to, że możesz przekazać dowolne dane do funkcji wysyłki, pozwolić na rozpakowanie danych i wywołanie odpowiedniej metody (metod) Ruby.

Na przykład, aby rb_protect wezwanie do metody Ruby, możesz użyć czegoś takiego:

#define MAX_ARGS 16 
struct my_callback_stuff { 
    VALUE obj; 
    ID method_id; 
    int nargs; 
    VALUE args[MAX_ARGS]; 
}; 

VALUE my_callback_dispatch(VALUE rdata) 
{ 
    struct my_callback_stuff* data = (struct my_callback_stuff*) rdata; 
    return rb_funcall2(data->obj, data->method_id, data->nargs, data->args); 
} 

... in some other function ... 
{ 
    /* need to call Ruby */ 
    struct my_callback_stuff stuff; 
    stuff.obj = the_object_to_call; 
    stuff.method_id = rb_intern("the_method_id"); 
    stuff.nargs = 3; 
    stuff.args[0] = INT2FIX(1); 
    stuff.args[1] = INT2FIX(2); 
    stuff.args[2] = INT2FIX(3); 

    int state = 0; 
    VALUE ret = rb_protect(my_callback_dispatch, (VALUE)(&stuff), &state); 
    if (state) { 
    /* ... error processing happens here ... */ 
    } 
} 

Ponadto, należy pamiętać, że rb_rescue lub rb_ensure może być lepszym rozwiązaniem dla niektórych problemów.

Powiązane problemy