Subject:
[ruby-ffi] Re: cannot define function pointers that take function pointer arguments in jruby struct layout
From:
Aman Gupta
Date:
9/21/09 4:45 PM
To:
ruby-ffi@googlegroups.com


On Thu, Sep 17, 2009 at 8:13 PM, Wayne Meissner <wmeissner@gmail.com> wrote:
>
> 2009/9/14 Aman Gupta <themastermind1@gmail.com>:
>>
>> I'm using ffi-swig-generator on:
>>
>> typedef void (*MyCallback) (void *data);
>> typedef struct {
>>  void (*member) (MyCallback cb);
>> } MyStruct;
>>
>> which generates:
>>
>>  callback(:MyCallback, [ :pointer ], :void)
>>  class MyStruct < FFI::Struct
>>    layout(
>>      :member, callback([ :MyCallback ], :void)
>>    )
>>  end
>
> The root problem here is the resolution of :MyCallback - it will fail
> on both JRuby and 1.9 (or at least it does for me).
>
> The best way to ensure this works is to define a constant in enclosing
> module, and use that on the layout line.
>
> e.g.
> module Foo
>  extend FFI::Library
>  MyCallback = callback(:MyCallback, [ :pointer ], :void
>  class MyStruct < FFI::Struct
>    layout(:member, callback(MyCallback, :void))
>  end
> end

I like this syntax, although it only works if the callback is capitalized.

>
> Alternatively you can look it up using Foo.find_type(:MyCallback)
>
> module Foo
>  extend FFI::Library
>  callback(:MyCallback, [ :pointer ], :void
>  class MyStruct < FFI::Struct
>    layout(:member, callback(Foo.find_type(:MyCallback), :void))
>  end
> end

This one is harder to implement, since ffi-swig-generator doesn't
necessarily know the name of the enclosing module.

>
> ffi-swig-generator should be altered to do one of the above.

How about:

diff --git a/lib/generator/function.rb b/lib/generator/function.rb
index e0b9ac2..7432747 100644
--- a/lib/generator/function.rb
+++ b/lib/generator/function.rb
@@ -40,7 +40,7 @@ module FFI
       end
       def to_s
         unless @inline
-          @indent_str + "callback(:#{@symname}, [
#{get_params.join(', ')} ], #{get_rtype})"
+          @indent_str + "CB_#{@symname} = callback(:#{@symname}, [
#{get_params.join(', ')} ], #{get_rtype})"
         else
           @indent_str + "callback([ #{get_params.join(', ')} ], #{get_rtype})"
         end
diff --git a/lib/generator/type.rb b/lib/generator/type.rb
index 8846f60..9c01202 100644
--- a/lib/generator/type.rb
+++ b/lib/generator/type.rb
@@ -99,7 +99,7 @@ module FFI
         ffi_type_from(Generator::TYPES['int']) if @declaration.is_enum?
       end
       def callback
-        ":#{@full_decl.scan(/^callback\s(\w+)/).flatten[0]}" if
@declaration.is_callback?
+        "CB_#{@full_decl.scan(/^callback\s(\w+)/).flatten[0]}" if
@declaration.is_callback?
       end
       def inline_callback
         Callback.new(:node => @node, :inline => true, :typedefs =>
@typedefs).to_s if @declaration.is_inline_callback?

This works for my use case, although I don't particularly like the CB_
prefix. Maybe C_, Callback_ or just C?

  Aman

>
> Struct#find_type tries to emulate this behaviour, and fails - ruby
> does not appear to have anyway of getting the lexical enclosing module
> of a subclass, when accessed from a method of the superclass.  If
> anyone knows a way, I'd be interested.
>