2010-05-10 6 views
3

J'écris une application qui appelle le code ruby ​​de c. J'ai un peu de difficulté et je me demandais si quelqu'un pouvait me pointer dans la direction du rite.Incorporation de Ruby, appel d'une fonction à partir de C

J'ai actuellement dans mon C.

#include ruby.h 

main() 
{ 
    ruby_init(); 
    rb_require("myRubyFile"); 
    rb_funcall(rb_module_new(), rb_intern("RubyFunction"), 0, NULL); 
} 

Mon fichier Ruby est dans le même répertoire que mon fichier c et est appelé myRubyFile.rb et contient une définition de la fonction RubyFunction().

Ceci est une réduction de ce que je veux réellement faire, juste le rendant plus lisible pour les autres. J'ai juste besoin de quelques commentaires quant à savoir si c'est la bonne méthode pour appeler le code ruby ​​de mon fichier c.

Cordialement

+0

suggestions rapides: 1) n'omettent ruby_init_loadpath() (je SEGV sans cela), 2) n » t nommez la méthode 'RubyFunction' - lorsqu'elle est appelée sans parens ruby, elle recherchera une * constante * du même nom, et 3) vous voudrez probablement une méthode" statique "définie dans une classe ou un module spécifique à l'application, pas une def'd contre le 'soi 'implicite (voir réponse). – pilcrow

Répondre

5

Réponse courte:

extern VALUE rb_vm_top_self(void); /* Assumes 1.9. Under 1.8, use the global 
            * VALUE ruby_top_self 
            */ 
... 
rb_funcall(rb_vm_top_self(),   /* irb> RubyFunction()     */ 
      rb_intern("RubyFunction"), /* irb> self.RubyFunction() # same thing */ 
      0, 
      NULL); 

réponse plus longue:

Le premier argument de rb_funcall est le récepteur de l'appel de méthode.

En supposant que vous def INED RubyFunction() en dehors de toute classe explicite ou contexte du module, il est ajouté aux eigenclass de l'implicite, objet principal au « haut niveau » de chaque vm rubis.

En rubis, cet objet est accessible en tant que premier niveau self:

$ cat myRubyFile.rb 
# file: myRubyFile.rb 
def foo 
    puts "foo" 
end 

$ irb 
irb> require "myRubyFile" 
=> true 
irb> foo 
foo 
=> nil 
irb> self.foo() # same thing, more explicit 
foo 
=> nil 
irb> self 
=> main 

In C sous 1,9 est accessible comme indiqué ci-dessus.

-1

Je ne vous aime à moins de ne pas appeler Ruby à l'intérieur C projet complexe C que vous ne voulez pas reconstruire en rubis.

Il existe deux façons d'interagir entre C et Ruby. Vous pouvez étendre ruby ​​avec du code écrit en C. Voir SWIG. Ou vous pouvez intégrer du rubis, voir here, here et here. BTW, qu'est-ce que vous mentionnez est "incorporer" rubis, pas "étendre" rubis.

0

je tente d'utiliser l'approche suivante:

struct Basic pour partager les données

typedef struct ruby_shared_data { 
    VALUE obj; 
    ID method_id; 
    int nargs; 
    VALUE args[4]; 
} ruby_shared_data; 

Créer une fonction pour les objets rubis d'appel sur une partie de votre code

static VALUE ruby_callback(VALUE ptr) { 

    ruby_shared_data *data = (ruby_shared_data*)ptr; 

    return rb_funcall2(data->obj,data->method_id,data->nargs,data->args); 
} 

Sur certains partie de votre code ...

ruby_shared_data rbdata; 

    rbdata.obj = obj; 
    rbdata.method_id = rb_intern("mycallback"); 
    rbdata.nargs = 1; 
    rbdata.args[0] = rb_str_new2("im a parameter"); 

    int error = 0; 
    VALUE result = rb_protect(ruby_callback,(VALUE)&rbdata,&error); 

    if (error) 
      throw "Ruby exception on callback"; 

Est toujours une bonne idée d'envelopper rb_funcall avec rb_protect.

Une autre chose intéressante est de connaître les paramètres de la fonction de rappel, une approche est la suivante

ruby_shared_data rbdata; 

rbdata.obj = callback; 
rbdata.method_id = rb_intern("arity"); 
rbdata.nargs = 0; 

int error = 0; 
VALUE result = rb_protect(ruby_callback,(VALUE)&rbdata,&error); 

if (error) 
     throw "Ruby exception on callback"; 

narguments = NUM2INT(result); 
Questions connexes