Hi,
I’m trying to get better at FFI, but I’m getting stumped on how to
create and pass pointers to functions.
In the example below, in the Sys::Uptime.seconds method, I’m trying to
figure out how to create and pass a pointer for the mib (a 2-element
integer array) and the timeval size (i.e. where I have a ‘?’ instead of
actual code).
What should the actual code be?
module Sys
class Uptime
extend FFI::Library
attach_function :time, [:pointer], :ulong
attach_function :sysctl,
[:pointer, :uint, :pointer, :pointer, :pointer, :uint], :int
CTL_KERN = 1 # Kernel
KERN_BOOTTIME = 21 # Time kernel was booted
class Timeval < FFI::Struct
layout(
:tv_sec, :long,
:tv_usec, :long
)
end
# How do I create the mib? How do I pass the address of tv.size?
def self.seconds
tv = Timeval.new
mib = [CTL_KERN, KERN_BOOTTIME]
# What's the proper way to call this?
if sysctl(?, 2, tv, ?, nil, 0) != 0
raise SystemCallError, 'sysctl()'
end
time(nil) - tv[:tv_sec]
end
end
end
Regards,
Dan
On Fri, Aug 7, 2009 at 10:22 AM, Daniel B.[email protected]
wrote:
At first sight you need MemoryPointer, but I could not really get it
working, but probably I screwed the sysctl call, I did not find a
reference of it for my system. Well for what it is worth I’ll share
what I did.
N.B. This fails with
ffi1.rb:35:in seconds': unknown error - sysctl() (SystemCallError) from ffi1.rb:43:in
’
def self.seconds
tv = Timeval.new
mib = [CTL_KERN, KERN_BOOTTIME]
# What's the proper way to call this?
tvp = FFI::MemoryPointer::new :pointer
tvp.put_pointer 0, tv
mip = FFI::MemoryPointer::new :int, 2
mip.put_array_of_int 0, mib
if sysctl(mip, 2, tv, tvp, nil, 0) != 0
raise SystemCallError, 'sysctl()'
end
time(nil) - tv[:tv_sec]
end
Maybe somebody can spot my error(s) 
Cheers
Robert
Daniel B. wrote:
Hi,
I’m trying to get better at FFI, but I’m getting stumped on how to
create and pass pointers to functions.
In the example below, in the Sys::Uptime.seconds method, I’m trying to
figure out how to create and pass a pointer for the mib (a 2-element
integer array) and the timeval size (i.e. where I have a ‘?’ instead of
actual code).
What should the actual code be?
You could explicity instantiate two MemoryPointer objects initialized
with
the proper values:
mib_ptr = FFI::MemoryPointer.new(:int, 2).write_array_of_int(mib)
tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)
Then you could pass those values to sysctl function:
sysctl(mib_ptr, 2, tv, tv_size_ptr, nil, 0)
To ask for more (and better) help feel free to post your questions to
the Ruby-FFI ml at [email protected]
2009/8/7 Andrea F. [email protected]:
[…]
tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)
[…]
Hello,
size_t in ANSI C is required to be unsigned and is usually defined as
unsigned long. Therefore the above code is always wrong in that it
stores a signed integer, and it is likely wrong on some 64 bit systems
where an int is 32 bits wide but a long is 64 bits wide.
cu,
Thomas
Andrea F. wrote:
sysctl(mib_ptr, 2, tv, tv_size_ptr, nil, 0)
Thank you, that worked.
To ask for more (and better) help feel free to post your questions to
the Ruby-FFI ml at [email protected]
If we want FFI to gain traction among Rubyists (we do, don’t we?), it’s
better to ask & answer here IMO.
Regards,
Dan
Thomas C. wrote:
2009/8/7 Andrea F. [email protected]:
�[…]
�tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)
[…]
Hello,
size_t in ANSI C is required to be unsigned and is usually defined as
unsigned long. Therefore the above code is always wrong in that it
stores a signed integer, and it is likely wrong on some 64 bit systems
where an int is 32 bits wide but a long is 64 bits wide.
cu,
Thomas
Hi Thomas,
I didn’t check for the sysctl prototype when replied to OP so I was not
aware about the type of the fourth argument (a pointer to a size_t
value). Thank you for pointing it out. That said, the MemoryPointer
object should be instantiated and filled in this way:
tv_size_ptr = FFI::MemoryPointer.new(:size_t).write_size_t(tv.size)
Doing this way, Ruby-FFI should use the right size for size_t.
Unfortunately, AFAIK, while :size_t type exists its accessors
(get_size_t, read_size_t, put_size_t, write_size_t) are not yet
implemented on Ruby-FFI. I’ll fire a JIRA ticket for this.
Please also note that my previous code is not always wrong. Well, it
may be conceptually wrong but not in practice for the considered case.
In fact, size of the Timeval struct is 8 bytes on a ILP32 system and 16
bytes on a LP64 one. Thus, writing/reading a pointer to a signed 8 (or
16) is the same as writing/reading a pointer to an unsigned 8 (or 16).
signed = FFI::MemoryPointer.new(:int).put_int(0, 8)
=> #
signed.get_int(0)
=> 8
signed.get_uint(0)
=> 8
unsigned = FFI::MemoryPointer.new(:uint).put_uint(0, 8)
=> #
unsigned.get_int(0)
=> 8
unsigned.get_uint(0)
=> 8
Andrea