¿Cómo se usa Ruby / DL? ¿Es esto correcto?
Pregunta
Estoy tratando de escribir una interfaz entre RSPEC (BDD con sabor a rubí) y una aplicación de Windows. La aplicación en sí está escrita en un lenguaje oscuro, pero tiene una API C para proporcionar acceso. Fui con Ruby / DL pero tengo dificultades para que funcione incluso la llamada más básica a un método DLL. Esto es lo que tengo hasta ahora, en un archivo llamado 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
Mi lectura hasta ahora sugiere que esto es todo lo que necesito para comenzar, así que escribí un ejemplo 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
Y cuando se ejecuta ...
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
El valor de retorno (13) es un código de retorno real, lo que significa un error, pero cuando intento agregar la llamada gTD_get_error_message a mi RSPEC, no puedo hacer que los parámetros funcionen.
¿Me dirijo en la dirección correcta y alguien puede señalar lo siguiente que puedo intentar?
Gracias Brett
Un seguimiento de esta pregunta, que muestra la parte que falla cuando intento obtener el mensaje de error de mi biblioteca de destino:
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
Espero que el mensaje de error sea devuelto en @msg, pero cuando lo ejecuto obtengo lo siguiente:
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.
Y esto si uso un símbolo (: msg) en su lugar:
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
Claramente me falta algo sobre pasar parámetros entre ruby ??y C, pero ¿qué?
Solución
El consenso general es que desea evitar DL tanto como sea posible. La documentación (en inglés) es bastante incompleta y la interfaz es difícil de usar para cualquier cosa que no sean ejemplos triviales.
La interfaz C nativa de Ruby es MUCHO más fácil de programar. O podría usar FFI, que llena un nicho similar a DL, originalmente proviene del proyecto rubinius y recientemente ha sido portado a "normal". rubí. Tiene una interfaz más agradable y es mucho menos doloroso de usar:
http://blog.headius.com/ 2008/10 / ffi-for-ruby-now-available.html
Otros consejos
El valor de retorno (13) es un valor real código de retorno, lo que significa un error, pero cuando trato de agregar el llamada a gTD_get_error_message a mi RSPEC, no puedo obtener los parámetros para trabajo.
Podría ayudar publicar el error en lugar del código que funcionó :)
Básicamente, una vez que comienzas a tener que lidiar con punteros como en (int, char **), las cosas se ponen feas.
Debe asignar el puntero de datos para que se escriba msg, ya que de lo contrario C no tendrá ningún lugar para escribir los mensajes de error. Use DL.mallo.