Reputation: 7719
I'm trying to dynamically call functions of external library on Linux/Unix system.
I have some success with dl
library but only when primitive C types are used and arguments are passed by value:
require 'dl/import'
module LibM
extend DL::Importer
dlload 'libm.so'
extern 'double sin(double)'
end
puts LibM.sin(3.14159265358979323846 / 2) # 1.0
However how can be interfaced functions using more complex types like C structs or if arguments are pointers where results of the call are stored ?
module LibX11
extend DL::Importer
dlload 'libX11.so.6'
extern 'Display *XkbOpenDisplay (char *display_name, int *event_rtrn, int *error_rtrn, int *major_in_out, int *minor_in_out, int *reason_rtrn)'
end
Display
is a big struct, at event_rtrn
is stored some result etc.
I've taken a look at DL::CStructBuilder and it looks can do the job, but as the documentation is very brief and no working examples found I'm lost here how it has to be properly used.
I have to add standard Ruby 1.9 modules have to be used (if possible) as installation of additional gems on the target machine is prohibited.
Upvotes: 3
Views: 844
Reputation: 8517
I am facing with interfacing with C libraries in this period (I am writing a wrapper for libgtop), and I choosed to use ffi, which is quite well documented (although the documentation sometimes is a bit outdated) and above all its mailing list is full of people who give you an helping hand when you are in trouble.
So I propose you a solution which uses ffi:
require 'ffi'
module LibX11
extend FFI::Library
ffi_lib 'libX11.so.6'
# Display *XkbOpenDisplay (char *display_name, int *event_rtrn, int *error_rtrn, int *major_in_out, int *minor_in_out, int *reason_rtrn)
attach_function :XkbOpenDisplay, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :pointer
end
Then you have to describe the Display
struct layout, like written here:
class Display < FFI::Struct
layout :value, :double,
:other_value, :char,
...
end
And then you do something like this:
p1 = FFI::MemoryPointer.new(:char)
p2 = FFI::MemoryPointer.new(:int)
p3 = FFI::MemoryPointer.new(:int)
p4 = FFI::MemoryPointer.new(:int)
p5 = FFI::MemoryPointer.new(:int)
p6 = FFI::MemoryPointer.new(:int)
# write to pointer if needed, otherwise it is a null pointer
# p1.write_char('a')
# p2.write_int(1)
# ...
struct_pointer = LibX11.XkbOpenDisplay(p1, p2, p3, p4, p5, p6)
# read the struct
struct = Display.new(display_struct_pointer)
p Hash[ s.members.map { |m| [ m, s[m] ] } ]
I didn't tested the code, but it should be roughly correct. Let me now if it isn't.
After some research about DL on ruby 2.0 I found that it is deprecated and replaced with fiddle, you would consider to use it rather than DL if you can't use FFI. Fiddle seems to be available for ruby 1.9.3 and 1.9.2 too
Upvotes: 2