Code Monkey home page Code Monkey logo

ffi-swig-generator's Introduction

Description

Ruby-FFI is a gem for programmatically loading dynamically-linked native libraries, binding functions within them, and calling those functions from Ruby code. Moreover, a Ruby-FFI extension works without changes on CRuby (MRI), JRuby, Rubinius and TruffleRuby. Discover why you should write your next extension using Ruby-FFI.

Features

  • Intuitive DSL
  • Supports all C native types
  • C structs (also nested), enums and global variables
  • Callbacks from C to Ruby
  • Automatic garbage collection of native memory
  • Usable in Ractor: How-to-use-FFI-in-Ruby-Ractors

Synopsis

require 'ffi'

module MyLib
  extend FFI::Library
  ffi_lib 'c'
  attach_function :puts, [ :string ], :int
end

MyLib.puts 'Hello, World using libc!'

For less minimalistic and more examples you may look at:

Requirements

When installing the gem on CRuby (MRI), you will need:

  • A C compiler (e.g., Xcode on macOS, gcc or clang on everything else) Optionally (speeds up installation):
  • The libffi library and development headers - this is commonly in the libffi-dev or libffi-devel packages

The ffi gem comes with a builtin libffi version, which is used, when the system libffi library is not available or too old. Use of the system libffi can be enforced by:

gem install ffi -- --enable-system-libffi        # to install the gem manually
bundle config build.ffi --enable-system-libffi   # for bundle install

or prevented by --disable-system-libffi.

On Linux systems running with PaX (Gentoo, Alpine, etc.), FFI may trigger mprotect errors. You may need to disable mprotect for ruby (paxctl -m [/path/to/ruby]) for the time being until a solution is found.

On FreeBSD systems pkgconf must be installed for the gem to be able to compile using clang. Install either via packages pkg install pkgconf or from ports via devel/pkgconf.

On JRuby and TruffleRuby, there are no requirements to install the FFI gem, and require 'ffi' works even without installing the gem (i.e., the gem is preinstalled on these implementations).

Installation

From rubygems:

[sudo] gem install ffi

From a Gemfile using git or GitHub

gem 'ffi', github: 'ffi/ffi', submodules: true

or from the git repository on github:

git clone git://github.com/ffi/ffi.git
cd ffi
git submodule update --init --recursive
bundle install
rake install

Install options:

  • --enable-system-libffi : Force usage of system libffi
  • --disable-system-libffi : Force usage of builtin libffi
  • --enable-libffi-alloc : Force closure allocation by libffi
  • --disable-libffi-alloc : Force closure allocation by builtin method

License

The ffi library is covered by the BSD license, also see the LICENSE file. The specs are covered by the same license as ruby/spec, the MIT license.

Credits

The following people have submitted code, bug reports, or otherwise contributed to the success of this project:

ffi-swig-generator's People

Contributors

bikenomad avatar davispuh avatar dmlary avatar galdor avatar phemmer avatar vp-of-awesome avatar windwiny avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ffi-swig-generator's Issues

Pointers to opaque structures do not work with attach_function()

Given the following C code:

struct opaque_struct;
struct opaque_struct *alloc_opaque_struct(void);

We currently generate the following ruby code:

class OpaqueStruct < FFI::Struct; end
attach_function :alloc_opaque_struct, :alloc_opaque_struct, [  ], OpaqueStruct.ptr

This code fails when calling alloc_opaque_struct() because FFI references the layout of the struct, which is not defined.

Instead, the following code should be generated:

attach_function :alloc_opaque_struct, :alloc_opaque_struct, [  ], :pointer

Typedefs for opaque structs being generated incorrectly

Given the following opaque struct definition:

struct opaque_struct;
typedef struct opaque_struct opaque_struct;

We get the following output

typedef OpaqueStruct.by_value, :opaque_struct

The FFI typedef command does not support this. It also does not support the following syntax:

typedef OpaqueStruct, :opaque_struct

In the case of opaque structs, no typedef line should be generated.

Function with va_list type argument

Not sure how this can be handled, but basically this function

void fn(const char *fmt, va_list vl);

swig generates as

<cdecl id="4456" addr="0x6efce8" >
                <attributelist id="4457" addr="0x6efce8" >
                    <attribute name="sym_name" value="fn" id="4458" addr="0xbf61b8" />
                    <attribute name="name" value="fn" id="4459" addr="0xbf61b8" />
                    <attribute name="decl" value="f(p.q(const).char,va_list)." id="4460" addr="0xbf61b8" />
                    <parmlist id="4461" addr="0x6efad8" >
                        <parm id="4462">
                            <attributelist id="4463" addr="0x6efc08" >
                                <attribute name="name" value="fmt" id="4464" addr="0xbf61b8" />
                                <attribute name="type" value="p.q(const).char" id="4465" addr="0xbf61b8" />
                            </attributelist >
                        </parm >
                        <parm id="4466">
                            <attributelist id="4467" addr="0x6efc98" >
                                <attribute name="name" value="vl" id="4468" addr="0xbf61b8" />
                                <attribute name="type" value="va_list" id="4469" addr="0xbf61b8" />
                            </attributelist >
                        </parm >
                    </parmlist >
                    <attribute name="kind" value="function" id="4470" addr="0xbf61b8" />
                    <attribute name="type" value="void" id="4471" addr="0xbf61b8" />
                    <attribute name="sym_symtab" value="0x6d0468" id="4472" addr="0x6d0468" />
                    <attribute name="sym_overname" value="__SWIG_0" id="4473" addr="0xbf61b8" />
                </attributelist >
</cdecl >

and it shows as

attach_function :fn, :fn, [ :string, va_list ], :void

which will fail with undefined local variable or method 'va_list'

I think currently could just comment it out or set va_list as :pointer. If someone really wants to use this, then I guess va_list type needs to be implemented in Ruby FFI

Array as function argument

When I've callback defined as

#define COUNT 10
void (*cl)(int list[COUNT]);

swig produces

<attribute name="decl" value="f(a(COUNT).int)." id="19616" addr="0xbf61b8" />

and it appears

callback([ a(COUNT).int ], :void)

but it probably should be

callback([ [:int, 10] ], :void)

only that's not supported yet, see ffi/ffi#385

callbacks not working after 14ea222

It appears commit 14ea222 broke callbacks.

Using the following input file:

%module Test
typedef void (*myfunc) (int foo);

With commit 818e921 I get the following result:

  Callback_myfunc = callback(:myfunc, [ :int ], :void)

With commit 14ea222 (and up to current master -- a018380) I get an empty file.

Enum values initialized with other enum members cause non-functional code.

Minimal Example:

%module test
%{
module Test
%}
enum foo {
FOO,
BAR = FOO
};
%{
end
%}

Generates to

module Test
  foo = enum :foo, [
    :foo,
    :bar, FOO, // fails here, obviously
  ]
  FOO = 0
  BAR = FOO
end

which doesn't work. I am not completely sure that ffi-swig-generator is at fault here, but it is my best idea.

Resolution of callback names in JRuby and MRI 1.9

Consider the C code below:

typedef void (*MyCallback) (void *data);
typedef struct {
  void (*member) (MyCallback cb);
} MyStruct;

Using the generator you get:

callback(:MyCallback, [ :pointer ], :void)
class MyStruct < FFI::Struct
  layout(
    :member, callback([ :MyCallback ], :void)
  )
end

The ruby code above works fine on MRI 1.8 but not on JRuby or MRI 1.9.

The best way to ensure this works is to generate a constant in enclosing module, and use that on the layout line.

module Foo
  extend FFI::Library
  MyCallback = callback(:MyCallback, [ :pointer ], :void
  class MyStruct < FFI::Struct
    layout(:member, callback(MyCallback, :void))
  end
end

Typedef'ed structs result in invalid arguments and struct members

Given the following C code:

typedef struct typedefed_struct {
  int i;
  char c;
} typedefed_struct;

struct other_struct {
  typedefed_struct s;
  int i;
};

int func(typedefed_struct *s);

ffi-swig-generator outputs the following Ruby code:

  class TypedefedStruct < FFI::Struct
    layout(
           :i, :int,
           :c, :char
    )
  end
  class OtherStruct < FFI::Struct
    layout(
           :s, :typedefed_struct,
           :i, :int
    )
  end
  attach_function :func, :func, [ :pointer ], :int

This code results in an error from ffi, `TypeError: unable to resolve type 'typedefed_struct'.

ffi-swig-generator should instead generate the following code:

  class TypedefedStruct < FFI::Struct
    layout(
           :i, :int,
           :c, :char
    )
  end
  class OtherStruct < FFI::Struct
    layout(
           :s, TypedefedStruct.by_value,
           :i, :int
    )
  end
  attach_function :func, :func, [ TypedefedStruct.ptr ], :int

Incorrect function pointer arguments within argument

Given the following code:

int cvStartLoop(int (*pt2Func)(int argc, char *argv[]), int argc, char* argv[]);

ffi-swig-generator outputs the following, invalid, ruby code:

attach_function :cvStartLoop, :cvStartLoop, [ callback([ :int, a().p.char ], :int), :int, :pointer ], :pointer

Note the a().p.char as the second argument within the callback.

ffi-swig-generator should output:

attach_function :cvStartLoop, :cvStartLoop, [ callback([:int, :pointer], :int), :int, :pointer ], :pointer

ffi-swig generating a :string instead of a :pointer

See: http://groups.google.com/group/ruby-ffi/browse_thread/thread/98eaf51ab0386347

I have a C++ function that takes a pointer to a buffer where the function stores a C-string.

This is what I wrote and got working.

attach_function :GoIO_GetNthAvailableDeviceName, [:pointer, :int, :int, :int, :int], :int

However if I use ffi-swig it generates a :string as the first parameter.

attach_function :GoIO_GetNthAvailableDeviceName, [ :string, :int, :int, :int, :int ], :int

Is there anyway this could work? The function needs an address to a location to store a C-string.

Here's the C++ function:

GOIO_DLL_INTERFACE_DECL gtype_int32 GoIO_GetNthAvailableDeviceName(
  char *pBuf,            // [out] ptr to buffer to store device name string.
  gtype_int32 bufSize,   // [in] number of bytes in buffer pointed to by pBuf.
                         // Strlen(pBuf) < bufSize, because the string is NULL terminated.
  gtype_int32 vendorId,  // [in] USB vendor id
  gtype_int32 productId, //[in] USB product id
  gtype_int32 N);        //[in] index into list of known devices, 0 => first device in list.

Looking more closely at ffi-swig it's type_spec.rb does expect:

char *string;

to be turned into a :string.

I'm not much of a C programmer but is that correct? It seems in this case it should be a pointer.

Types like these are correctly turned into :string

const char *string;

The choice about turning a pointer into a string appears to be made in ffi-swig at lib/generator/type.rb:74:

def pointer
  if @declaration.is_pointer? or @is_pointer > 0
    @is_pointer += 1
    if @full_decl.scan(/^p\.(.+)/).flatten[0]
      ffi_type_from(@full_decl.scan(/^p\.(.+)/).flatten[0])
    elsif @full_decl == 'char' and @is_pointer == 2
      ':string'
    else
      ':pointer'
    end
  end       
end

Enum types using :int instead of Enum class

When generating ruby for the following code:

enum test_enum { FIRST, SECOND, THIRD };
struct test_struct {
  enum test_enum test_value;
}

ffi-gen outputs the following ffi struct:

class TestStruct < FFI::Struct
  layout(
    :test_enum, :int,
  )
end

It should generate the following struct instead:

class TestStruct < FFI::Struct
  layout(
    :test_enum, TestStruct,
  )
end

CLI: ffi-gen --version (-v) -> uninitialized constant FFI::Generator::VERSION

ffi-gen -v

/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2503:in const_missing': uninitialized constant FFI::Generator::VERSION (NameError) from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:39:indo_option'
from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:48:in process_args' from /usr/lib/ruby/1.8/getoptlong.rb:613:ineach'
from /usr/lib/ruby/1.8/getoptlong.rb:610:in loop' from /usr/lib/ruby/1.8/getoptlong.rb:610:ineach'
from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:48:in process_args' from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:22:inrun'
from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/bin/ffi-gen:6
from /usr/bin/ffi-gen:19:in load' from /usr/bin/ffi-gen:19 localhost:~ # ffi-gen --version /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2503:inconst_missing': uninitialized constant FFI::Generator::VERSION (NameError)
from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:39:in do_option' from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:48:inprocess_args'
from /usr/lib/ruby/1.8/getoptlong.rb:613:in each' from /usr/lib/ruby/1.8/getoptlong.rb:610:inloop'
from /usr/lib/ruby/1.8/getoptlong.rb:610:in each' from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:48:inprocess_args'
from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/lib/generator/application.rb:22:in run' from /usr/lib/ruby/gems/1.8/gems/ffi-swig-generator-0.3.2/bin/ffi-gen:6 from /usr/bin/ffi-gen:19:inload'
from /usr/bin/ffi-gen:19

unnamed enums

Hi!
I have this code:

typedef enum week {
   SUNDAY,
   MONDAY,
   TUESDAY,
   WEDNESDAY,
   THURSDAY,
   FRIDAY,
   SATURDAY
} week;

and I'm expecting that generator will create named enum:

 week = enum(
      :sunday,
      :monday,
      :tuesday,
      :wednesday,
      :thursday,
      :friday,
      :saturday)

but it doesn't:

MONDAY = 0
TUESDAY = 1
...

Is it possible to fix it?

Pointer typedefs generated incorrectly

Pointer typedefs are not being generated correctly. Given the following c typedef:

typedef int * pInt

This is the ruby code that is generated:

typedef :int, :pInt

This is what should be generated:

typedef :pointer, :pInt

Typedefs for pointers to opaque structs

A typedef for a pointer to an opaque struct results in invalid ruby being generated.

Given the following C code:

typedef struct opaque_struct* opaque_pointer;
opaque_pointer func(void);

ffi-swig-generator outputs the following ruby:

attach_function :func, :func, [  ], :opaque_pointer

The line defining the :opaque_pointer as a :pointer is missing. The output should be as follows:

typedef :pointer, :opaque_pointer
attach_function :func, :func, [  ], :opaque_pointer

String constants are generated without quotes

If my header file has this:

#define FOO "foo"

Then the generated ruby file has this:

FOO = foo

In other words, foo is not quoted.

I think that adding the following to the case statement in the sanitize! method in Constant will address this (though not certain about nested quotes)

when 'p.char'
      result = "\"#{@value}\""

Swig %rename(...) not being respected

If I use %rename() in a Swig .i file, the resultant XML has the new identifiers in the sym_name attributes (and the original names in the name attributes). But ffi-swig-generator keeps the original names, despite the fact that FFI's attach_function() can take an alternative name.

A one-line change to lib/generator/function.rb can fix this:

diff --git a/lib/generator/function.rb b/lib/generator/function.rb
index 8a26dd2..1483485 100644
--- a/lib/generator/function.rb
+++ b/lib/generator/function.rb
@@ -21,7 +21,7 @@ module FFI
         params = get_params(@node).inject([]) do |array, node|
           array << Argument.new(:node => node, :typedefs => @typedefs).to_s
         end
-        @indent_str + "attach_function :#{@symname}, [ #{params.join(', ')} ], #{get_rtype}"
+        @indent_str + "attach_function :#{get_attr('sym_name')}, :#{@symname}, [ #{params.join(', ')} ], #{get_rtype}"
       end
       private
       def get_params(node)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.