Как вы используете Ruby / DL?Правильно ли это?
Вопрос
Я пытаюсь написать интерфейс между RSPEC (BDD со вкусом ruby) и приложением Windows.Само приложение написано на непонятном языке, но у него есть C API для обеспечения доступа.Я перешел на Ruby / DL, но у меня возникают трудности с запуском даже самого простого вызова метода DLL для работы.Вот что у меня есть на данный момент, в файле под названием 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
Мое чтение на данный момент предполагает, что это все, что мне нужно для начала, поэтому я написал пример 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
И когда бежишь...
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
Возвращаемое значение (13) является фактическим кодом возврата, означающим ошибку, но когда я пытаюсь добавить вызов gTD_get_error_message в мой 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
Gt4r.gTD_get_error_message rv, @msg
@msg.should == ""
rv.should == 0
end
end
Я ожидаю, что сообщение об ошибке будет возвращено в @msg, но при запуске я получаю следующее:
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.
И это, если я использую вместо этого символ (:msg):
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
Очевидно, я что-то упускаю из виду в передаче параметров между ruby и C, но что?
Решение
Общий консенсус заключается в том, что вы хотите избегать DL, насколько это возможно.Документация (на английском языке) довольно схематична, а интерфейс трудно использовать для чего-либо, кроме тривиальных примеров.
Собственный интерфейс Ruby C намного проще программировать на основе.Или вы могли бы использовать FFI, который занимает аналогичную нишу с DL, изначально взят из проекта rubinius и недавно был портирован на "обычный" ruby.Он имеет более приятный интерфейс и гораздо менее болезнен в использовании:
http://blog .headius.com/2008/10/ffi-for-ruby-now-available.html
Другие советы
Возвращаемое значение (13), является актуальной код возврата, что означает ошибка, но когда я пытаюсь добавить gTD_get_error_message звонок на мой RSPEC, я не могу заставить параметры работать .
Это могло бы помочь опубликовать ошибку вместо кода, который сработал :)
По сути, как только вам начинает иметь дело с указателями, как в (int, char **), все становится некрасиво.
Вам необходимо выделить указатель данных для msg, в который будет записываться сообщение, поскольку в противном случае C некуда будет записывать сообщения об ошибках.Используйте DL.mallo.