Comment utilisez-vous Ruby / DL? Est-ce correct?
Question
J'essaie d'écrire une interface entre RSPEC (BDD à saveur de rubis) et une application Windows. L'application elle-même est écrite dans un langage obscur, mais elle dispose d'une API C pour fournir un accès. J'ai opté pour Ruby / DL, mais j'ai du mal à faire fonctionner même l'appel le plus élémentaire d'une méthode DLL. Voici ce que j'ai jusqu'à présent, dans un fichier nommé gt4r.rb:
require 'dl/import'
module Gt4r
extend DL::Importable
dlload 'c:\\gtdev\\r321\\bin\\gtvapi'
# GTD initialization/termination functions
extern 'int GTD_init(char *[], char *, char *)'
extern 'int GTD_initialize(char *, char *, char *)'
extern 'int GTD_done(void)'
extern 'int GTD_get_error_message(int, char **)'
end
Mes lectures jusqu'à présent suggèrent que c'est tout ce dont j'avais besoin pour commencer, alors j'ai écrit un exemple de RSPEC:
require 'gt4r'
@@test_environment = "INCLUDE=C:\\graphtalk\\env\\aiadev\\config\\aiadev.ini"
@@normal_user = "BMCHARGUE"
describe Gt4r do
it 'initializes' do
rv = Gt4r.gTD_initialize @@normal_user, @@normal_user, @@test_environment
rv.should == 0
end
end
Et lorsqu’il est exécuté ...
C:\code\GraphTalk>spec -fs -rgt4r gt4r_spec.rb
Gt4r
- initializes (FAILED - 1)
1)
'Gt4r initializes' FAILED
expected: 0,
got: 13 (using ==)
./gt4r_spec.rb:9:
Finished in 0.031 seconds
1 example, 1 failure
La valeur de retour (13) est un code de retour réel, ce qui signifie une erreur, mais lorsque j'essaie d'ajouter l'appel gTD_get_error_message à mon RSPEC, les paramètres ne fonctionnent pas.
Est-ce que je vais dans la bonne direction et quelqu'un peut-il indiquer la prochaine chose que je peux essayer?
Merci, Brett
Un suivi de cette question, montrant la partie qui échoue lorsque j'essaie d'obtenir le message d'erreur de ma bibliothèque cible:
require 'gt4r'
@@test_environment = "INCLUDE=C:\\graphtalk\\env\\aiadev\\config\\aiadev.ini"
@@normal_user = "BMCHARGUE"
describe Gt4r do
it 'initializes' do
rv = Gt4r.gTD_initialize @@normal_user, @@normal_user, @@test_environment
Gt4r.gTD_get_error_message rv, @msg
@msg.should == ""
rv.should == 0
end
end
Je m'attends à ce que le message d'erreur soit renvoyé dans @msg, mais lors de l'exécution, le message suivant s'affiche:
Gt4r
(eval):5: [BUG] Segmentation fault
ruby 1.8.6 (2008-08-11) [i386-mswin32]
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Et ceci si j'utilise un symbole (: msg) à la place:
C:\code\GraphTalk\gt4r_dl>spec -fs -rgt4r gt4r_spec.rb
Gt4r
- initializes (ERROR - 1)
1)
NoMethodError in 'Gt4r initializes'
undefined method `to_ptr' for :msg:Symbol
(eval):5:in `call'
(eval):5:in `gTD_get_error_message'
./gt4r_spec.rb:9:
Finished in 0.046 seconds
1 example, 1 failure
Clairement, il me manque quelque chose à propos du passage de paramètres entre ruby ??et C, mais quoi?
La solution
Le consensus général est que vous voulez éviter le plus possible la DL. La documentation (anglaise) est assez sommaire et l'interface est difficile à utiliser, sauf pour des exemples triviaux.
L’interface C native de Ruby est BEAUCOUP plus facile à programmer. Vous pouvez également utiliser FFI, qui remplit un créneau similaire à DL, provient à l'origine du projet rubinius et a récemment été porté à "normal". rubis. Il a une interface plus agréable et est beaucoup moins pénible à utiliser:
http://blog.headius.com/ 2008/10 / ffi-for-ruby-now-available.html
Autres conseils
La valeur de retour (13) est une valeur réelle. retourne le code, ce qui signifie une erreur, mais quand j'essaie d'ajouter le gTD_get_error_message appel à mon RSPEC, je ne peux pas obtenir les paramètres travail.
Cela pourrait aider de poster l'erreur au lieu du code qui a fonctionné :)
En gros, une fois que vous commencez à avoir à gérer des pointeurs comme dans (int, char **), les choses deviennent laides.
Vous devez allouer le pointeur de données sur lequel écrire le message, car otherise C n'aura nulle part où écrire les messages d'erreur. Utilisez DL.mallo.