Subject:
Re: [ruby-ffi] Passing a struct by value, and typedef
From:
Maurizio De Santis
Date:
3/10/13 11:50 AM
To:
ruby-ffi@googlegroups.com

Thank you, the porting is going well; if you want you can see the progress at https://github.com/ProGNOMmers/gtop .

Now I have some troubles: I need to use g_free from glib, so I tried to wrap it:

require 'gtop'

module GTop
  module GLib
    extend FFI::Library
    ffi_lib 'libglib2.0'

    typedef :pointer, :gpointer

    attach_function :g_free, [:gpointer], :void
  end
end


On my pc libglib2.0.so is located inside /usr/lib/x86_64-linux-gnu/ , which should be found since it is inside the ld paths, which on my machine are

# Multiarch support
/lib/i386-linux-gnu
/usr/lib/i386-linux-gnu
/lib/i686-linux-gnu
/usr/lib/i686-linux-gnu
# libc default configuration
/usr/local/lib
/usr/lib/nvidia-settings
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/mesa


but I get a LoadError:

Could not open library 'libglib2.0': libglib2.0: cannot open shared object file: No such file or directory.
Could not open library 'libglib2.0.so': libglib2.0.so: cannot open shared object file: No such file or directory
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/library.rb:123:in `block in ffi_lib'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/library.rb:90:in `map'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/library.rb:90:in `ffi_lib'
/home/user/github/me/gtop/lib/gtop/glib.rb:6:in `<module:GLib>'
/home/user/github/me/gtop/lib/gtop/glib.rb:4:in `<module:GTop>'
/home/user/github/me/gtop/lib/gtop/glib.rb:3:in `<top (required)>'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/home/user/github/me/gtop/lib/gtop.rb:7:in `<module:GTop>'
/home/user/github/me/gtop/lib/gtop.rb:3:in `<top (required)>'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/home/user/github/me/gtop/Rakefile:4:in `block in <top (required)>'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:228:in `call'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:228:in `block in execute'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:223:in `each'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:223:in `execute'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:166:in `block in invoke_with_call_chain'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:159:in `invoke_with_call_chain'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:152:in `invoke'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:143:in `invoke_task'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:101:in `block (2 levels) in top_level'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:101:in `each'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:101:in `block in top_level'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:110:in `run_with_threads'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:95:in `top_level'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:73:in `block in run'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:160:in `standard_exception_handling'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:70:in `run'
/home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/bin/rake:33:in `<top (required)>'
/home/user/.rbenv/versions/2.0.0-p0/bin/rake:23:in `load'
/home/user/.rbenv/versions/2.0.0-p0/bin/rake:23:in `<main>'


I tried to set LD_LIBRARY_PATH too, but without success.

Oh, and I have another doubt: gpointer is the return value of `glibtop_get_proclist', and can be either `NULL' on error or a `unsigned *' list of pids.
I defined it as :pointer, it is correct? could the NULL value give problems? should I manage it?

--

Maurizio De Santis


2013/3/5 Wayne Meissner <wmeissner@gmail.com>

:buffer_in, and :buffer_out are optimization hints for VMs when they need to copy data from VM heap memory to native memory:

:buffer_in means "allocate some temporary native memory, copy the contents of this object to it, then pass the temporary memory address to the function"

:buffer_out means "allocate some temporary native memory, pass that address to the function, then afterwards, copy the contents of the temporary memory area back to the VM heap object"

:pointer and :buffer_inout are analogous and mean "do both of the above" - i.e. copies both in to and out of the temporary memory area.

If you're using a MemoryPointer as your memory (as Struct does), then it is already native, so it doesn't matter, so you can just use :pointer.

The original rationale behind the :buffer_in and :buffer_out directives was that allocating VM heap objects is really cheap, and most VMs have a very hard time managing native memory allocations.  So from an implementation point of view, it was better to bias towards VM heap allocations and copy data in/out of temporary native memory.  Thats where FFI::Buffer came from - it was VM heap memory that was always copied to/from native memory when passed as a parameter.

In practice, people found it difficult and confusing to understand, so I just made native allocation management work as best it could in JRuby-1.7.


tl;dr - just use :pointer - it will work just fine, at an occasional minor performance penalty.


On Monday, 4 March 2013 11:45:38 UTC+10, Postmodern wrote:
You want :pointer in this case. I believe :buffer_out is a meant as a hint to FFI that data will be written back into an Array of chars/ints.

On 03/03/2013 05:41 PM, Maurizio De Santis wrote:
Another question: glibtop_get_cpu takes a struct as parameter, and fills it with informations about cpu.

I declared it as

attach_function :glibtop_get_cpu, [:pointer], :void

I think I can substitute :pointer with :buffer_out , am I right?

--

Maurizio De Santis


2013/3/4 Maurizio De Santis <desantis...@gmail.com>
Thank you a lot, your suggestions are precious.

--

Maurizio De Santis


2013/3/3 postmodern <postmod...@gmail.com>
You can simplify that code to just:
a = LibGtop::GlibtopCpu.new
LibGtop.glibtop_get_cpu(a)
p a[:frequency]
This will allocate memory for a GlibtopCpu struct, pass the pointer to the memory to glibtop_get_cpu and access to the struct field within the memory. Once you are done with the "a" variable, Ruby will just GC it and FFI will free the underlying memory.


On 03/02/2013 07:54 PM, Maurizio De Santis wrote:
Thank you. Now, the code runs without errors:

require 'ffi'

module LibGtop
 
  extend FFI::Library
  ffi_lib 'libgtop-2.0'

  typedef :uint64, :guint64

  attach_function :glibtop_get_cpu, [:pointer], :void

  class GlibtopCpu < FFI::Struct
    layout :flags,         :guint64,
           :total,         :guint64,
           :user,          :guint64,
           :nice,          :guint64,
           :sys,           :guint64,
           :idle,          :guint64,
           :iowait,        :guint64,
           :irq,           :guint64,
           :softirq,       :guint64,
           :frequency,     :guint64,
           :xcpu_total,   [:guint64, 32],
           :xcpu_user,    [:guint64, 32],
           :xcpu_nice,    [:guint64, 32],
           :xcpu_sys,     [:guint64, 32],
           :xcpu_idle,    [:guint64, 32],
           :xcpu_iowait,  [:guint64, 32],
           :xcpu_irq,     [:guint64, 32],
           :xcpu_softirq, [:guint64, 32],
           :xcpu_flags,    :guint64
  end

end


# taken from https://github.com/ffi/ffi/wiki/Structs
pointer = FFI::MemoryPointer.new :uint64, LibGtop::GlibtopCpu.size, true
a = LibGtop::GlibtopCpu.new pointer
LibGtop.glibtop_get_cpu(a)
p a[:frequency]


Another question: I initialized the pointer with clear = true, because the struct pointed by the pointer gets filled by glibtop_get_cpu, and then must be managed by Ruby, since glibtop_get_cpu just fills the struct. Am I wrong?

--

Maurizio De Santis


2013/3/3 postmodern <postmod...@gmail.com>
You need to find out guint64's underlying type, and create the typedef in Ruby:

module LibGtop
  extend FFI::Library

  typedef :uint64, :guint64
end

On 03/02/2013 06:06 PM, Maurizio De Santis wrote:
I am writing a libgtop2 wrapper using ffi in order to learn how to use ffi; I am new to ffi, so I have a lot of doubts.

libgtop2 lets get system and processes informations (cpu usage, memory usage, ...).

Here some informations about glibtop_get_cpu:

Library function `glibtop_get_cpu':

     void glibtop_get_cpu (glibtop_cpu *buf);
     void glibtop_get_cpu_l (glibtop *server, glibtop_cpu *buf);

   Declaration of `glibtop_cpu' in `<glibtop/cpu.h>':

     typedef struct _glibtop_cpu     glibtop_cpu;

     struct _glibtop_cpu
     {
         guint64   flags,
             total,
             user,
             nice,
             sys,
             idle,
             iowait,
             irq,
             softirq,
             frequency,
             xcpu_total [GLIBTOP_NCPU],
             xcpu_user [GLIBTOP_NCPU],
             xcpu_nice [GLIBTOP_NCPU],
             xcpu_sys  [GLIBTOP_NCPU],
             xcpu_idle [GLIBTOP_NCPU],
             xcpu_iowait [GLIBTOP_NCPU],
             xcpu_irq [GLIBTOP_NCPU],
             xcpu_softirq [GLIBTOP_NCPU],
             xcpu_flags;
     };

...



Here is what I wrote:

require 'ffi'

class GlibtopCpu < FFI::Struct
  layout :flags,        :guint64,
         :total,        :guint64,
         :user,         :guint64,
         :nice,         :guint64,
         :sys,          :guint64,
         :idle,         :guint64,
         :iowait,       :guint64,
         :irq,          :guint64,
         :softirq,      :guint64,
         :frequency,    :guint64,
         :xcpu_total,   :guint64,
         :xcpu_user,    :guint64,
         :xcpu_nice,    :guint64,
         :xcpu_sys,     :guint64,
         :xcpu_idle,    :guint64,
         :xcpu_iowait,  :guint64,
         :xcpu_irq,     :guint64,
         :xcpu_softirq, :guint64,
         :xcpu_flags,   :guint64
end

module LibGtop
 
  extend FFI::Library
  ffi_lib 'libgtop-2.0'
  attach_function :glibtop_get_cpu, [:pointer], :void

end

# taken from https://github.com/ffi/ffi/wiki/Structs
pointer = FFI::MemoryPointer.new :byte, GlibtopCpu.size, false
a = GlibtopCpu.new pointer
p LibGtop.glibtop_get_cpu(a)


Executing this gives (unsurprisingly) an error:

$ ruby lib/lib_gtop.rb
/home/izietto/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/types.rb:57:in `find_type': unable to resolve type 'guint64' (TypeError)
    from /home/izietto/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/struct.rb:316:in `find_type'
    from /home/izietto/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/struct.rb:309:in `find_field_type'
    from /home/izietto/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/struct.rb:351:in `array_layout'
    from /home/izietto/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/ffi-1.4.0/lib/ffi/struct.rb:261:in `layout'
    from lib/lib_gtop.rb:4:in `<class:GlibtopCpu>'
    from lib/lib_gtop.rb:3:in `<main>'


It doesn't find the guint64 type; how should I declare it?

Also: xcpu_total, xcpu_user are arrays... how should I declare them?

Finally... if someone could give me an explanation and an example of how to implement the bind to glibtop_get_cpu, I would appreciate it very much :)
--
 
---
You received this message because you are subscribed to the Google Groups "ruby-ffi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-ffi+u...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 


-- 
Blog: http://postmodern.github.com/
GitHub: https://github.com/postmodern
Twitter: @postmodern_mod3
PGP: 0xB9515E77

--
 
---
You received this message because you are subscribed to the Google Groups "ruby-ffi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-ffi+u...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 


-- 
Blog: http://postmodern.github.com/
GitHub: https://github.com/postmodern
Twitter: @postmodern_mod3
PGP: 0xB9515E77


--
 
---
You received this message because you are subscribed to the Google Groups "ruby-ffi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-ffi+u...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 


-- 
Blog: http://postmodern.github.com/
GitHub: https://github.com/postmodern
Twitter: @postmodern_mod3
PGP: 0xB9515E77

--
 
---
You received this message because you are subscribed to a topic in the Google Groups "ruby-ffi" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ruby-ffi/sG-TsAHcWiM/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to ruby-ffi+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 

--
 
---
You received this message because you are subscribed to the Google Groups "ruby-ffi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-ffi+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.