GithubHelp home page GithubHelp logo

ruby / fiddle Goto Github PK

View Code? Open in Web Editor NEW
152.0 38.0 37.0 613 KB

A libffi wrapper for Ruby.

License: BSD 2-Clause "Simplified" License

C 45.91% Ruby 53.06% Makefile 0.79% Shell 0.09% Emacs Lisp 0.01% Dockerfile 0.14%
ruby libffi fiddle

fiddle's Introduction

Fiddle

CI

A libffi wrapper for Ruby.

Fiddle is an extension to translate a foreign function interface (FFI) with ruby.

It wraps libffi, a popular C library which provides a portable interface that allows code written in one language to call code written in another language.

Installation

Add this line to your application's Gemfile:

gem 'fiddle'

And then execute:

$ bundle

Or install it yourself as:

$ gem install fiddle

Usage

Here we will use Fiddle::Function to wrap floor(3) from libm

require 'fiddle'

libm = Fiddle.dlopen('/lib/libm.so.6')

floor = Fiddle::Function.new(
          libm['floor'],
          [Fiddle::TYPE_DOUBLE],
          Fiddle::TYPE_DOUBLE
        )

puts floor.call(3.14159) #=> 3.0

Nested Structs

You can use hashes to create nested structs, where the hash keys are member names and the values are the nested structs:

StudentCollegeDetail = struct [
  'int college_id',
  'char college_name[50]'
]

StudentDetail = struct [
  'int id',
  'char name[20]',
  { clg_data: StudentCollegeDetail }
]

You can also specify an anonymous nested struct, like so:

StudentDetail = struct [
  'int id',
  'char name[20]',
  {
    clg_data: struct([
                      'int college_id',
                      'char college_name[50]'
                    ])
  }
]

The position of a hash (and the order of the keys in the hash, in the case of a hash with multiple entries), dictate the offsets of the nested struct in memory. The following examples are both syntactically valid but will lay out the structs differently in memory:

# order of members in memory: position, id, dimensions
Rect = struct [ { position: struct(['float x', 'float y']) },
                'int id',
                { dimensions: struct(['float w', 'float h']) }
              ]

# order of members in memory: id, position, dimensions
Rect = struct [ 'int id',
                {
                  position: struct(['float x', 'float y']),
                  dimensions: struct(['float w', 'float h'])
                }
              ]

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/fiddle.

License

The gem is available as open source under the terms of the BSD-2-Clause.

fiddle's People

Contributors

akr avatar casperisfine avatar chrisseaton avatar dependabot[bot] avatar drbrain avatar eban avatar eregon avatar extrowerk avatar hsbt avatar jeremyevans avatar k-tsj avatar k0kubun avatar ko1 avatar kou avatar mame avatar matzbot avatar mrkn avatar msp-greg avatar nagachika avatar nobu avatar nurse avatar olleolleolle avatar shugo avatar shyouhei avatar sinisterchipmunk avatar tadd avatar tenderlove avatar unak avatar voxik avatar znz 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  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  avatar  avatar

Watchers

 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

fiddle's Issues

Cannot parse the signature of the const argument.

Step to reproduce

t.c

int add(const int x, const int y){
  int sum = x + y;
  return sum;
}

gcc t.c -o add.so -fPIC -shared

t.rb

require 'fiddle/import'

module F
 extend Fiddle::Importer
 dlload "./add.so"
 extern "int add(const int x, const int y)"
end

p F.add(1, 2)

ruby t.rb

Actual behavior

Error

/home/kojix2/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/fiddle-1.0.8/lib/fiddle/cparser.rb:245:in `parse_ctype': unknown type: const (Fiddle::DLError)
	from /home/kojix2/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/fiddle-1.0.8/lib/fiddle/cparser.rb:117:in `block in parse_signature'
	from /home/kojix2/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/fiddle-1.0.8/lib/fiddle/cparser.rb:117:in `collect'
	from /home/kojix2/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/fiddle-1.0.8/lib/fiddle/cparser.rb:117:in `parse_signature'
	from /home/kojix2/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/fiddle-1.0.8/lib/fiddle/import.rb:168:in `extern'
	from t.rb:6:in `<module:F>'
	from t.rb:3:in `<main>'

Expected behavior

require 'fiddle/import'

module F
 extend Fiddle::Importer
 dlload "./add.so"
 extern "int add(int x, int y)" # If you remove const, it works as expected
end

p F.add(1, 2) #=> 3

Why cannot I define a struct made of nested structs only?

I have reduced an issue I am having with Fiddle to this test:

require 'fiddle'
require 'fiddle/import'

module Pos
  extend Fiddle::Importer
  dlload Fiddle.dlopen(nil)
    
  Point = struct "int x, int y"
  ABL = struct "{a: Point}, {b: Point}, int len"
  ALB = struct "{a: Point}, int len, {b: Point}"
  LAB = struct "int len, {a: Point}, {b: Point}"
  begin
    AB = struct "{a: Point}, {b: Point}"
  rescue Exception => e
    puts "Receiving error: #{e}"
  end
end

that gives me Receiving error: nil can't be coerced into Integer

add slice method to Fiddle::MemoryView

# Fiddle::MemoryView#slice(start, length)
# Fiddle::MemoryView#slice(range)
mv = Fiddle::MemoryView.new(memory_object)

while mv
  puts mv[0]
  mv = mv.slice(1..-1) # returns a new MemoryView object with the same MemoryView#obj   # that is no copying
end

Gem overwritten on rubygems.org?

I've been tracking down why my build is failing, and it appears that all old versions of fiddle on rubygems.org have been yanked.

Looking closer, it appears that this is a completely different gem to the previous one, which appears to be https://github.com/bsm/fiddle.

What's happened to the old gem?

"already initialized constant" warnings

Microsoft Windows [Version 10.0.19042.630]
(c) 2020 Microsoft Corporation. All rights reserved.

C:\Users\xmr\Desktop>ruby -v && gem -v
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x64-mingw32]
3.1.4

C:\Users\xmr\Desktop>gem update --system
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_VOID
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_VOIDP
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_CHAR
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_SHORT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_INT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_LONG
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_LONG_LONG
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_FLOAT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_DOUBLE
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_SIZE_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_SSIZE_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_PTRDIFF_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_INTPTR_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::TYPE_UINTPTR_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_VOIDP
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_CHAR
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_SHORT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_INT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_LONG
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_LONG_LONG
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_FLOAT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_DOUBLE
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_SIZE_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_SSIZE_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_PTRDIFF_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_INTPTR_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::ALIGN_UINTPTR_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::WINDOWS
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_VOIDP
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_CHAR
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_SHORT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_INT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_LONG
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_LONG_LONG
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_FLOAT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_DOUBLE
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_SIZE_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_SSIZE_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_PTRDIFF_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_INTPTR_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::SIZEOF_UINTPTR_T
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::RUBY_FREE
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::BUILD_RUBY_PLATFORM
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::Function::DEFAULT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::Handle::NEXT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::Handle::DEFAULT
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::Handle::RTLD_GLOBAL
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::Handle::RTLD_LAZY
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::Handle::RTLD_NOW
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.so: warning: already initialized constant Fiddle::NULL
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.rb:55: warning: already initialized constant Fiddle::RTLD_GLOBAL
C:/Ruby27-x64/lib/ruby/2.7.0/fiddle.rb:53: warning: previous definition of RTLD_GLOBAL was here
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.rb:56: warning: already initialized constant Fiddle::RTLD_LAZY
C:/Ruby27-x64/lib/ruby/2.7.0/fiddle.rb:54: warning: previous definition of RTLD_LAZY was here
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/fiddle-1.0.2/lib/fiddle.rb:57: warning: already initialized constant Fiddle::RTLD_NOW
C:/Ruby27-x64/lib/ruby/2.7.0/fiddle.rb:55: warning: previous definition of RTLD_NOW was here
Latest version already installed. Done.

Installation of fiddle failing on Ruby 2.7.3

I failed to install fiddle 1.0.9 on Ruby 2.7.3 built from source. Here is the full log.

$ cat /etc/fedora-release 
Fedora release 34 (Thirty Four)

$ gcc --version
gcc (GCC) 11.1.1 20210531 (Red Hat 11.1.1-3)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ which ruby
/usr/local/ruby-2.7.3/bin/ruby

$ ruby -v
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-linux]

$ gem install --no-document fiddle --user
Fetching fiddle-1.0.9.gem
Building native extensions. This could take a while...
ERROR:  Error installing fiddle:
	ERROR: Failed to build gem native extension.

    current directory: /home/jaruga/.gem/ruby/2.7.0/gems/fiddle-1.0.9/ext/fiddle
/usr/local/ruby-2.7.3/bin/ruby -I /usr/local/ruby-2.7.3/lib/ruby/2.7.0 -r ./siteconf20210704-791519-1jr4m15.rb extconf.rb
checking for --enable-debug-build option... no
checking for ffi.h... yes
...
checking for ruby/memory_view.h... yes
checking for rb_memory_view_t in ruby/memory_view.h... no
creating Makefile

current directory: /home/jaruga/.gem/ruby/2.7.0/gems/fiddle-1.0.9/ext/fiddle
make "DESTDIR=" clean

current directory: /home/jaruga/.gem/ruby/2.7.0/gems/fiddle-1.0.9/ext/fiddle
make "DESTDIR="
compiling closure.c
compiling conversions.c
compiling fiddle.c
compiling function.c
compiling handle.c
compiling memory_view.c
In file included from memory_view.c:6:
/usr/include/ruby/memory_view.h: In function ‘RBIMPL_ATTR_PURE’:
/usr/include/ruby/memory_view.h:125:1: error: expected declaration specifiers before ‘RBIMPL_ATTR_PURE’
  125 | RBIMPL_ATTR_PURE()
      | ^~~~~~~~~~~~~~~~
...
make: *** [Makefile:246: memory_view.o] Error 1

make failed, exit code 2

Gem files will remain installed in /home/jaruga/.gem/ruby/2.7.0/gems/fiddle-1.0.9 for inspection.
Results logged to /home/jaruga/.gem/ruby/2.7.0/extensions/x86_64-linux/2.7.0/fiddle-1.0.9/gem_make.out

It seems the fiddle trying to find the memory_view.h file. But I could not find the header file on Ruby 2.7.3. Seeing the ruby/ruby, the header file is created by ruby/ruby#3261 in 2020.

$ find /usr/local/ruby-2.7.3/ -name memory_view.h

The header file exists on Ruby 3.0.1 And On Ruby 3.0.1, The installation of the fiddle succeeds.

$ find /usr/local/ruby-3.0.1/ -name memory_view.h
/usr/local/ruby-3.0.1/include/ruby-3.0.0/ruby/memory_view.h

On Ubuntu 21.04, fiddle cannot load libffi.so.7

$ ldd /home/pierre/.rbenv/versions/2.7.3/lib/ruby/2.7.0/x86_64-linux/fiddle.so
	linux-vdso.so.1 (0x00007ffed952b000)
	libruby.so.2.7 => /home/pierre/.rbenv/versions/2.7.3/lib/libruby.so.2.7 (0x00007fa76e2d2000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa76e2bb000)
	libffi.so.7 => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa76e0cf000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fa76e0b3000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa76e091000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa76e084000)
	libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007fa76e04a000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa76defc000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa76e6b4000)

Because Ubuntu 21.04 come with libffi.so.8

$ ls /lib/x86_64-linux-gnu/libffi*
/lib/x86_64-linux-gnu/libffi.a      /lib/x86_64-linux-gnu/libffi.so    /lib/x86_64-linux-gnu/libffi.so.8.1.0
/lib/x86_64-linux-gnu/libffi_pic.a  /lib/x86_64-linux-gnu/libffi.so.8

Question: When passing a char as a function argument, should I pass the number obtained by calling the string's ord method?

Hi!
I have a question about c char.

https://github.com/andlabs/libui/blob/fea45b2d5b75839be0af9acc842a147c5cba9295/ui.h#L767

c function

void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value);

ruby method

extern 'void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value)'

I wonder if I should call ord like this?

LibUI::FFI.uiOpenTypeFeaturesAdd(otf, 'l'.ord, 'i'.ord, 'g'.ord, 'a'.ord, 0)

Thank you.

JRuby support

JRuby ships an implementation of fiddle based on FFI, and cannot support the C extension contained in this gem. It may be possible for us to do what @chrisseaton did and add a native fiddle implementation, but for now we would like to incorporate our own fiddle implementation into this gem, so that we can use it and users can reference it.

If you call a member of a structure many times, the value will change.

Hi @kou and fiddle developers!

When I reference a member of a structure multiple times with Fiddle, I find that the value of the member of the structure changes.

The following sample has a high probability of changing values in my environment. (Not always)

require 'fiddle/import'

module A
  extend Fiddle::Importer
  S = struct [
    'int8_t* hoge',
    'int8_t* fuga']
end

s = A::S.malloc
s.hoge = [*1..10].pack('c*')
s.fuga = [*1..10].reverse.pack('c*')

a1 = s.fuga[0,10].unpack('c*')

1000.times do
  s.fuga[0,10].unpack('c*')
end

b1 = s.fuga[0,10].unpack('c*')

if a1 == b1
  puts "OK"
else
  p a1, b1
end

I have confirmed that this problem occurs in the following environment.

  • Ruby 2.7, 3.0 + Ubuntu + fiddle 1.0.7
  • Ruby 2.6 + Mac + fiddle 1.0.7

I also asked people in ruby-jp slack to try it. The problem is reproducible.

Thank you.

about Fiddle.free

I'm not very familiar with C language, But I need to call it, This is my C code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


char* hello(char* name) {
    char* say = (char*)"Hello ";
    char* welcome = (char*)malloc(strlen(say) + strlen(name));
    sprintf(welcome, "%s%s", say, name);
    return welcome;
}

char* hello2(char* name) {
    char* say = (char*)"Hello ";
    char* welcome = (char*)malloc(strlen(say) + strlen(name));
    sprintf(welcome, "%s%s", say, name);
    return say;
}

This is my Ruby code:

require 'fiddle'
require 'fiddle/import'

module Lib
  extend Fiddle::Importer
  dlload './77.dll'

  extern 'char* hello(char*)'
end

s = "fa"
a = Lib.hello(s)


Fiddle.free(a.to_i)


f = "asd"
c = Lib.hello2(s)

Fiddle.free(c.to_i)

Fiddle.free(a.to_i) is working fine, But call Fiddle.free(c.to_i),ruby will crash. My question is whether we have to use Fiddle.free to release it? and why Fiddle.free(c.to_i) will crash

Directory structure problem for ruby/ruby

I merged the current master branch to ruby/ruby repository. But the master branch was failed with make test-all caused by directory structure.

ruby/ruby#2603

I reverted the above commits manually. I'm going to investigate it. If you have any idea, Please fix this directory.

Thanks!

How to import Kernel32 function 'GetVolumePathName', with Windows types?

The code:

module WFFI
  require 'fiddle'
  require 'fiddle/import'
  require 'fiddle/types'
  extend Fiddle::Importer
  dlload 'kernel32'
  include Fiddle::Win32Types
  extern "BOOL WINAPI GetVolumePathName(LPCTSTR lpszFileName, LPTSTR lpszVolumePathName, DWORD cchBu\
fferLength);"
end

Error:

C:/tools/ruby24/lib/ruby/2.4.0/fiddle/cparser.rb:177:in `parse_ctype': unknown type: LPCTSTR (Fiddle::DLError)
        from C:/tools/ruby24/lib/ruby/2.4.0/fiddle/cparser.rb:90:in `block in parse_signature'
        from C:/tools/ruby24/lib/ruby/2.4.0/fiddle/cparser.rb:90:in `collect'
        from C:/tools/ruby24/lib/ruby/2.4.0/fiddle/cparser.rb:90:in `parse_signature'
        from C:/tools/ruby24/lib/ruby/2.4.0/fiddle/import.rb:166:in `extern'
        from fiddle.rb:10:in `<module:WFFI>'
        from fiddle.rb:3:in `<main>'

I will try with WIN32API now since I cannot discover the solution so far.

Notes:
There's a number of errors that can happen before I got this far:

  • If you don't 'extend FFI::Importer' first, include Fiddle::Win32Types raises an error (NoMethodError typealias). I have a suggestion: Fiddle::Win32Importer.
    It is Win32Types+Importer module. I can PR if you like the idea.

  • If you don't call dlload before include Fiddle::Win32Types, there is another NoMethodError. I think point mentioned above would solve also. Remove the chances of error while using this library.

Thanks for your work!

Release and merge into MRI

We've had several fairly significant functionality PRs recently - at least #33, #36, #38, #41, #44.

Might now be the time to make a release and merge into MRI as they start preparing for 3.0?

The clone method of Fiddle::CStruct object is shallow copy

Fiddle

require 'fiddle/import'

S = Fiddle::Importer.struct(["int i"])

a = S.malloc

a.i = 10

b = a.clone

b.i = 20

p a.i # 20
p b.i # 20

Ruby-FFI

require 'ffi'

class S < FFI::Struct
  layout :i, :int
end

a = S.new

a[:i] = 10

b = a.clone

b[:i] = 20

p a[:i] # 10
p b[:i] # 20

Unhelpful error message

rescue DLError

When dlload in import.rb fails to load it raises an DLError with the message "can't load #{lib}". This is not a very helpful message as it doesn't say anything about why it couldn't load the library.

I've commented out some code for rescuing and re-raising so I could see the original error message, which to me was much more helpful.

Current:

          begin
            Fiddle.dlopen(lib)
          rescue DLError
            raise(DLError, "can't load #{lib}")
          end

Modified:

          #begin
            Fiddle.dlopen(lib)
          #rescue DLError
          #  raise(DLError, "can't load #{lib}")
          #end

Perhaps the original message can be included in the new one, or rescuing and re-raising could be removed in favor of the original exception.

Some background information: I'm very new to both Fiddle and C/C++ programming, but have been using Ruby for years. I've (tried to) compile my first ever DLL and use it in a Ruby project using Fiddle. When getting an error saying "Can't load", it is very hard to tell if I did something wrong with my DLL, if I've provided the wrong path or if I am using Fiddle incorrectly in any way.

It is possible to create nested structures using fiddle

It seems that fiddle, can make structs of simple c types but not nesting structures, exists a way of doing so? how can I express this structure in c:

struct student_college_detail
{
    int college_id;
    char college_name[50];
};
 
struct student_detail 
{
    int id;
    char name[20];
    float percentage;
    // structure within structure
    struct student_college_detail clg_data;
}stu_data;

Could not require Fiddle in ruby.

when I try to require fiddle
irb -U -r fiddle
it says:
/home/kun/.rvm/gems/ruby-2.7.0/gems/irb-1.2.3/lib/irb/init.rb:290: warning: LoadError: libffi.so.6: cannot open shared object file: No such file or directory - /home/kun/.rvm/rubies/ruby-2.7.0/lib/ruby/2.7.0/x86_64-linux/fiddle.so
to deal with it, I tried to downgrade libffi version from 3.3.3 to 3.2.1 and from the first view it looked ok, but my visual code stopped working and was reporting similar issue.
my system parameters:
OS : Arch Linux 5.6.4
Ruby : 2.7.0, with rvm
Libffi : 3.3.3
Fiddle gem : 1.0.0

memoryview extension in ruby

Is it possible to have a memoryview extension for ruby instead of using fiddle? Otherwise consider memoryview support in ruby/ffi also

cannot update gem (from 1.9.25) on macOS Catalina

macOS Catalina, version 10.15.7. gem update fiddle produces the following:

compiling function.c
function.c:285:22: error: implicit declaration of function 'ffi_prep_cif_var' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
            result = ffi_prep_cif_var(args.cif,
                     ^
function.c:285:22: note: did you mean 'ffi_prep_cif'?
/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/ffi/ffi.h:312:1: note: 'ffi_prep_cif' declared here
ffi_prep_cif(
^
1 error generated.
make: *** [function.o] Error 1

make failed, exit code 2

any help?

Unexpected error code with WSAGetLastError even though the previous function calll was failed

The problem

There is a case that fiddle fails to get error code with WSAGetLastEror on Win32.

This problem is observed with Ruby 3.0 only.

Expected result

WSAGetLastError returns correct error code when previous function call was failed.

Actual result

WSAGetLastError returns 0 even thought when previous function call was failed.

  • Windows 10 20H2 (19042.870)
  • Ruby installer with fiddle 1.0.7

How to reproduce

Here is the sample code (test-wsagetlasterror.rb) to reproduce issue.

module WinSock
  require 'fiddle/import'
  extend Fiddle::Importer
  dlload 'ws2_32.dll'
  extern 'int bind(int, void *, int)'
  extern 'int WSAGetLastError(void)'
end

p WinSock.bind(0, nil, 0)
p WinSock.WSAGetLastError

When using with Ruby 2.7 https://github.com/oneclick/rubyinstaller2/releases/download/RubyInstaller-2.7.2-1/rubyinstaller-2.7.2-1-x64.exe
WinSock.WSAGetLastError returns 10038 (expected)

PS C:\work\fluentd> ridk use
1 - C:/Ruby25-x64       ruby 2.5.8p224 (2020-03-31 revision 67882) [x64-mingw32]
2 - C:/Ruby26-x64       ruby 2.6.6p146 (2020-03-31 revision 67876) [x64-mingw32]
3 - C:/Ruby27-x64       ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x64-mingw32]
4 - C:/Ruby30-x64       ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x64-mingw32]
Select ruby version to enable: 3
Disable C:/Ruby26-x64
Disable C:/Ruby30-x64
Enable C:/Ruby27-x64
PS C:\work\fluentd> ruby .\test-wsagetlasterror.rb
-1
10038

When using with Ruby 3.0 https://github.com/oneclick/rubyinstaller2/releases/download/RubyInstaller-3.0.0-1/rubyinstaller-3.0.0-1-x64.exe,
WinSock.WSAGetLastError returns 0 (not expected)

PS C:\work\fluentd> ridk use
1 - C:/Ruby25-x64       ruby 2.5.8p224 (2020-03-31 revision 67882) [x64-mingw32]
2 - C:/Ruby26-x64       ruby 2.6.6p146 (2020-03-31 revision 67876) [x64-mingw32]
3 - C:/Ruby27-x64       ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x64-mingw32]
4 - C:/Ruby30-x64       ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x64-mingw32]
Select ruby version to enable: 4
Disable C:/Ruby26-x64
Disable C:/Ruby27-x64
Enable C:/Ruby30-x64
PS C:\work\fluentd> ruby .\test-wsagetlasterror.rb
-1
0

Segfault when calling `dlopen` on certain Qt frameworks

I have Qt installed via Homebrew on macOS 12 (x86_64), and running the following script causes a segmentation fault (ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-darwin21] installed via rbenv, Fiddle 1.1.0 installed via gem):

require 'fiddle'

dylibs = [
  '/usr/local/lib/Qt3DCore.framework/Qt3DCore',
  '/usr/local/lib/Qt3DQuickAnimation.framework/Qt3DQuickAnimation',
  '/usr/local/lib/Qt3DQuickExtras.framework/Qt3DQuickExtras'
]

dylibs.each do |dylib|
  puts dylib
  Fiddle.dlopen(dylib).close
end

Running ruby crash.rb produces the following output (truncated for brevity):

/usr/local/lib/Qt3DCore.framework/Qt3DCore
/usr/local/lib/Qt3DQuickAnimation.framework/Qt3DQuickAnimation
/usr/local/lib/Qt3DQuickExtras.framework/Qt3DQuickExtras
/Users/carlocab/.rbenv/versions/2.6.8/lib/ruby/gems/2.6.0/gems/fiddle-1.1.0/lib/fiddle.rb:61: [BUG] Segmentation fault at 0x000000010f18945c
ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-darwin21]

-- Crash Report log information --------------------------------------------
   See Crash Report log file under the one of following:
     * ~/Library/Logs/DiagnosticReports
     * /Library/Logs/DiagnosticReports
   for more details.
Don't forget to include the above Crash Report log file in bug reports.

-- Control frame information -----------------------------------------------
c:0007 p:---- s:0028 e:000027 CFUNC  :initialize
c:0006 p:---- s:0025 e:000024 CFUNC  :new
c:0005 p:0014 s:0020 e:000019 METHOD /Users/carlocab/.rbenv/versions/2.6.8/lib/ruby/gems/2.6.0/gems/fiddle-1.1.0/lib/fiddle.rb:61
c:0004 p:0020 s:0015 e:000014 BLOCK  crash.rb:11 [FINISH]
c:0003 p:---- s:0011 e:000010 CFUNC  :each
c:0002 p:0023 s:0007 E:001288 EVAL   crash.rb:9 [FINISH]
c:0001 p:0000 s:0003 E:001650 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
crash.rb:9:in `<main>'
crash.rb:9:in `each'
crash.rb:11:in `block in <main>'
/Users/carlocab/.rbenv/versions/2.6.8/lib/ruby/gems/2.6.0/gems/fiddle-1.1.0/lib/fiddle.rb:61:in `dlopen'
/Users/carlocab/.rbenv/versions/2.6.8/lib/ruby/gems/2.6.0/gems/fiddle-1.1.0/lib/fiddle.rb:61:in `new'
/Users/carlocab/.rbenv/versions/2.6.8/lib/ruby/gems/2.6.0/gems/fiddle-1.1.0/lib/fiddle.rb:61:in `initialize'

-- Machine register context ------------------------------------------------
 rax: 0x000000010f189450 rbx: 0x0000000000000000 rcx: 0x0000000000000120
 rdx: 0x0000000000000000 rdi: 0x00006000034343b0 rsi: 0x00006000034065b0
 rbp: 0x00007ff7b2096b30 rsp: 0x00007ff7b2096ae0  r8: 0x0000000000000065
  r9: 0x000000010fdd2556 r10: 0x0000000100000600 r11: 0x0000000000000000
 r12: 0x000000010f1ef450 r13: 0x000000010f1be298 r14: 0x00007ff7b2096b48
 r15: 0x000000010fe450c0 rip: 0x000000010fa9ddf7 rfl: 0x0000000000010206

-- C level backtrace information -------------------------------------------
/Users/carlocab/.rbenv/versions/2.6.8/lib/libruby.2.6.dylib(rb_vm_bugreport+0x82) [0x10e562652]
/Users/carlocab/.rbenv/versions/2.6.8/lib/libruby.2.6.dylib(rb_bug_context+0x1d6) [0x10e3bb906]
/Users/carlocab/.rbenv/versions/2.6.8/lib/libruby.2.6.dylib(sigsegv+0x51) [0x10e4c9511]
/usr/lib/system/libsystem_platform.dylib(_sigtramp+0x1d) [0x7ff811cb5e2d]
/usr/local/Cellar/qt/6.2.1/lib/QtCore.framework/Versions/A/QtCore(_ZNK9QMetaType8idHelperEv+0xb7) [0x10fa9ddf7]
/usr/local/Cellar/qt/6.2.1/lib/Qt3DQuick.framework/Versions/A/Qt3DQuick(_GLOBAL__sub_I_quick3dbuffer.cpp+0x26) [0x10f1cd8b6]

The full output can be found here: output.txt
The log referenced in the error message: crash.log.tar.gz

I can only produce the segfault when the dylibs array is dlopened in the order given. Dropping one of them or reversing the array allows the script to run correctly.

As a sanity check, I wrote what I think should be the equivalent program in C:

#include <dlfcn.h>
#include <stdio.h>

int main()
{
    char dylibs[][65] = {
        "/usr/local/lib/Qt3DCore.framework/Qt3DCore",
        "/usr/local/lib/Qt3DQuickAnimation.framework/Qt3DQuickAnimation",
        "/usr/local/lib/Qt3DQuickExtras.framework/Qt3DQuickExtras",
    };

    for (int i = 0; i < 3; ++i) {
        printf("%s\n", dylibs[i]);
        void *handle;
        handle = dlopen(dylibs[i], RTLD_LAZY | RTLD_GLOBAL);
        if (handle == NULL) {
            printf("failed to load %s\n", dylibs[i]);
            return 1;
        }
        dlclose(handle);
    }

    return 0;
}

This runs without any problems:

❯ make crash && ./crash; echo $?
cc     crash.c   -o crash
/usr/local/lib/Qt3DCore.framework/Qt3DCore
/usr/local/lib/Qt3DQuickAnimation.framework/Qt3DQuickAnimation
/usr/local/lib/Qt3DQuickExtras.framework/Qt3DQuickExtras
0

Debug support in fiddle?

kojix2 provides the fiddle-bindings for libui here:

https://github.com/kojix2/LibUI

Libui is very easy to use; I ported many of my ruby-gtk3 widgets already,
even though libui is quite limited compared to ruby-gtk3. Anyway, in my own
project i modify Fiddle::Pointer to fake-"simulate" OOP.

My biggest problem with fiddle so far is the debug-support. Due to doing
tons of mistakes I get lots of segfaults, and while it is fun to find out why,
in the long run I'd love to be able to more easily and quickly find the error
I did. But when things segfault, I don't seem to get good quality information.

If segfaults happen in ruby, then I often get the exact name, stack tracke
and call stack; and I can use caller(). With fiddle I do not seem to get the
same information or at the least not as much as I think may be useful.

Would it be possible to improve the debug support in fiddle in general?
Perhaps a better stack trace showing where errors happened, a bit
like how ruby does. Or perhaps I just don't know how to debug this,
in which case it would be nice if the main README could provide a
few helpful pointers. Please feel free to close this issue at any moment
in time - I understand that this may not be a trivial change but if anyone
finds some time in the future, keeping better debug-support would be
super-useful. Right now I seem to do random changes and hope for
the best. :D (See also kojix2' comment in regards to having to assign
to variables to avoid the GC terminate the program. Would be nice
if this could all become more transparent when working via ruby.)

Can't call variadic function

Hi fiddle developers!

I'm trying to call a function from Ruby that takes variable length arguments in C.
I asked some Ruby developers how to do it , and they said that this feature is not implemented in fiddle.
I would appreciate it if you would consider implementing it.

Thank you.

Pseudoclasses via ruby-fiddle?

Hello fiddle-folks,

fiddle is quite epic. kojix2 wrote the bindings (if you can call them) for libui - see here for
most of the try_extern calls:

https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb

As a "downstream" user, though, fiddle is not easy to use. Often I just get segfaults and
then I end up not understanding why. I can figure out indirectly to some extent and with
helpful comments (e. g. GC kicking in), but by and large I'd love if ruby-fiddle would have
more debug-friendly support - similar to did-you-mean gem or the recent debug-related
changes by mame in MRI ruby.

Having worked with ruby-gtk a lot I am kind of used to modify core widgets such as
Gtk::Windows or Gtk::Box (horizontal and vertical). I like simple APIs such as .add()
or << just to add widgets together, a bit like LEGO building blocks.

With fiddle, though, this is a bit complicated. So the core question for this issue is
whether fiddle could allow for "designated" pseudoclasses? I'd love to build up
classes that can simulate the Gtk widgets to some extent (at the least what libui
allows for; see also libui-ng that has been recently started).

Something where I can build "pseudoclasses" and then at the least the error
messages can be more helpful. Rather than a segfault as such, something
that allows us to investigate which line is faulty or why it crashed (e. g. GC
kicking in). kojix2 showed various work arounds (see the examples he provides
for ruby-libui) but this requires some more expert knowledge than many ruby
developers have. For instance I do not really know C. And while I should really
have learned C properly, ruby is simply more fun to work with! :D

I do not know how easy it would be to have something like pseudoclasses
work, and better error/debug support for fiddle. It's ok if the downstream
dev has to write more code to enable this - I am not really suggesting an
"autofixer" that solves all problems as-is. But something with better debug-support
and to use fiddle in a more ruby-way, including a traditional OOP-like hierarchy.
Getting random SEGFAULTS and not really knowing why can be frustrating.

Other than that fiddle is pretty cool. (ruby-gtk via gobject-introspection also
has this problem partially by the way - the error messages I get are often
not super-useful.)

Can't open linker script files

On my Ubuntu 21.10 installation, several shared libraries .so files are installed as linker scripts instead of using a symlink. In my research, I see that this is done (or has been done) in Debian, CentOS, Fedora, RHEL, Gentoo, and maybe Arch (I didn't personally verify the last two). This shouldn't a big deal if you link with GNU ld, but it breaks dlopen.

I found workarounds in the ffi gem and in ghc, and perhaps one of those could be copied into fiddle.

A workaround for users of fiddle might be to attempt to load a specific version of the library:

irb(main):005:0> File.read "/lib/x86_64-linux-gnu/libncurses.so"
=> "INPUT(libncurses.so.6 -ltinfo)\n"
irb(main):006:0> Fiddle::Handle.new("libncurses.so")
(irb):6:in `initialize': /lib/x86_64-linux-gnu/libncurses.so: file too short (Fiddle::DLError)
	from (irb):6:in `new'                              
	from (irb):6:in `<main>'                           
	from /home/nick/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
	from /home/nick/.rbenv/versions/3.1.1/bin/irb:25:in `load'
	from /home/nick/.rbenv/versions/3.1.1/bin/irb:25:in `<main>'
irb(main):007:0> Fiddle::Handle.new("libncurses.so.6")
=> #<Fiddle::Handle:0x00007fcfbb858670>

Do not require_relative in gemspec

@kou

Please do not use require_relative "lib/fiddle/version". This causes constant redefinition warning:

http://rubyci.s3.amazonaws.com/ubuntu2004/ruby-master/log/20200523T153003Z.log.html.gz

[ 6317/20193] TestDefaultGems#test_validate_gemspec/home/chkbuild/chkbuild/tmp/build/20200523T153003Z/ruby/ext/fiddle/lib/fiddle/version.rb:2: warning: already initialized constant Fiddle::VERSION
/home/chkbuild/chkbuild/tmp/build/20200523T153003Z/ruby/.ext/common/fiddle/version.rb:2: warning: previous definition of VERSION was here
 = 0.16 s

stringio and zlib uses a hack to extract the version by regexp:

https://github.com/ruby/stringio/blob/05d75e5e6688a9667d3ab8128d307e3f4b66b616/stringio.gemspec#L4-L12

I'm not a big fan of this hack, but it works without a warning. Tentatively, I've committed the same hack to ruby/ruby.

ruby/ruby@f27e0d9

Are you satisfied with the hack? If so, I'll create a PR for backport. If not, please let me know how to suppress the warning.

Thanks!

Fiddle Closure ABI

Include information about what an ABI is in docs regarding Fiddle Closure constructor, perhaps even include definitions below in Fiddle::ABI?

typedef enum ffi_abi {
#if defined(X86_WIN64)
  FFI_FIRST_ABI = 0,
  FFI_WIN64,            /* sizeof(long double) == 8  - microsoft compilers */
  FFI_GNUW64,           /* sizeof(long double) == 16 - GNU compilers */
  FFI_LAST_ABI,
#ifdef __GNUC__
  FFI_DEFAULT_ABI = FFI_GNUW64
#else  
  FFI_DEFAULT_ABI = FFI_WIN64
#endif  

#elif defined(X86_64) || (defined (__x86_64__) && defined (X86_DARWIN))
  FFI_FIRST_ABI = 1,
  FFI_UNIX64,
  FFI_WIN64,
  FFI_EFI64 = FFI_WIN64,
  FFI_GNUW64,
  FFI_LAST_ABI,
  FFI_DEFAULT_ABI = FFI_UNIX64

#elif defined(X86_WIN32)
  FFI_FIRST_ABI = 0,
  FFI_SYSV      = 1,
  FFI_STDCALL   = 2,
  FFI_THISCALL  = 3,
  FFI_FASTCALL  = 4,
  FFI_MS_CDECL  = 5,
  FFI_PASCAL    = 6,
  FFI_REGISTER  = 7,
  FFI_LAST_ABI,
  FFI_DEFAULT_ABI = FFI_MS_CDECL
#else
  FFI_FIRST_ABI = 0,
  FFI_SYSV      = 1,
  FFI_THISCALL  = 3,
  FFI_FASTCALL  = 4,
  FFI_STDCALL   = 5,
  FFI_PASCAL    = 6,
  FFI_REGISTER  = 7,
  FFI_MS_CDECL  = 8,
  FFI_LAST_ABI,
  FFI_DEFAULT_ABI = FFI_SYSV
#endif
} ffi_abi;
#endif

Document CStruct vs CStructEntity

I understand what CStructEntity is - it's a subclass of pointer that points to an instance of the structure in memory.

I don't really understand what CStruct is. It's an object that has a method that returns that subclass of pointer that you want to instantiate. Why not just pass the subclass of pointer in? Why have this entity_class indirection?

Could we write Fiddle::CStructBuilder.create(CUnion, types, members) instead of Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)?

I know about backwards compatibility concerns, but I don't understand the original intention behind this, and it isn't documented, and even as an FFI and Ruby expert I find it extremely confusing.

I also wish CStructEntity was CStructPointer, but again backwards compatibility I guess.

ERROR: Failed to build gem native extension.

Problem

I'm hated the fact that default Linux package distributor don't update the package quickly enough. So is the case for RUBY. Then I came to think why not kill this problem with Homebrew . And I installed Ruby with Brew which works nice. Not a problem. But recently I got update of RUBY 3.0.0 and problem starts. With the fact I use Ruby with brew. The fiddle need a dependency called ffi and ffi is also served by apt. I don't know much but the problem is ffi.

Expected

The gem should be normally installed without any issue.

Actual

user@machine:~$ gem update
Updating installed gems
Updating fiddle
Building native extensions. This could take a while...
ERROR:  Error installing fiddle:
	ERROR: Failed to build gem native extension.

    current directory: /home/linuxbrew/.linuxbrew/lib/ruby/gems/3.0.0/gems/fiddle-1.0.7/ext/fiddle
/home/linuxbrew/.linuxbrew/opt/ruby/bin/ruby -I /home/linuxbrew/.linuxbrew/Cellar/ruby/3.0.0_1/lib/ruby/3.0.0 -r ./siteconf20210104-8354-iij1al.rb extconf.rb
checking for ffi.h... no
checking for ffi/ffi.h... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
	--with-opt-dir
	--with-opt-include
	--without-opt-include=${opt-dir}/include
	--with-opt-lib
	--without-opt-lib=${opt-dir}/lib
	--with-make-prog
	--without-make-prog
	--srcdir=.
	--curdir
	--ruby=/home/linuxbrew/.linuxbrew/Cellar/ruby/3.0.0_1/bin/$(RUBY_BASE_NAME)
	--enable-bundled-libffi
	--disable-bundled-libffi
	--with-libffi-dir
	--without-libffi-dir
	--with-libffi-include
	--without-libffi-include=${libffi-dir}/include
	--with-libffi-lib
	--without-libffi-lib=${libffi-dir}/lib
	--with-libffi-config
	--without-libffi-config
	--with-pkg-config
	--without-pkg-config
	--with-ffi-dir
	--without-ffi-dir
	--with-ffi-include
	--without-ffi-include=${ffi-dir}/include
	--with-ffi-lib
	--without-ffi-lib=${ffi-dir}/lib
extconf.rb:46:in `<main>': missing libffi. Please install libffi. (RuntimeError)

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /home/linuxbrew/.linuxbrew/lib/ruby/gems/3.0.0/extensions/x86_64-linux/3.0.0/fiddle-1.0.7/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /home/linuxbrew/.linuxbrew/lib/ruby/gems/3.0.0/gems/fiddle-1.0.7 for inspection.
Results logged to /home/linuxbrew/.linuxbrew/lib/ruby/gems/3.0.0/extensions/x86_64-linux/3.0.0/fiddle-1.0.7/gem_make.out
Gems updated: fiddle

What to do now?
mkmf.log
gem_make.out.log rename due to upload issue.

Ruby test suite failures with libffi-3.4.2

Hi, libffi-3.4.2 recently landed in Fedora and since that time, I observe strange failures in Ruby test suite:

  1) Failure:
TestAutoload#test_autoload_fork [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_autoload.rb:380]:
[ruby-core:86410] [Bug #14634].
Expected #<Test::Unit::AssertionFailedError: Expected #<Process::Status: pid 3249430 SIGABRT (signal 6) (core dumped)> to be success?.> to be nil.
  2) Failure:
TestAutoload#test_autoload_fork [/builddir/build/BUILD/ruby-3.1.0/tool/lib/zombie_hunter.rb:6]:
Expected [[3249431, #<Process::Status: pid 3249431 SIGABRT (signal 6) (core dumped)>]] to be empty.
  3) Failure:
TestRand#test_fork_shuffle [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_rand.rb:276]:
#<Process::Status: pid 3249809 SIGABRT (signal 6) (core dumped)>.
Expected #<Process::Status: pid 3249809 SIGABRT (signal 6) (core dumped)> to be success?.
  4) Failure:
TestRand#test_rand_reseed_on_fork [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_rand.rb:306]:
[ruby-core:41209]
pid 3249817 killed by SIGABRT (signal 6) (core dumped)
  5) Failure:
TestIO#test_copy_stream_socket7 [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_io.rb:995]:
Expected #<Process::Status: pid 3251775 SIGABRT (signal 6) (core dumped)> to be success?.
  6) Failure:
JSONGeneratorTest#test_broken_bignum [/builddir/build/BUILD/ruby-3.1.0/test/json/json_generator_test.rb:305]:
Failed assertion, no message given.
  7) Failure:
TestBeginEndBlock#test_internal_errinfo_at_exit [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_beginendblock.rb:175]:
Expected #<Process::Status: pid 3252235 SIGABRT (signal 6) (core dumped)> to not be signaled?.
  8) Failure:
TestProcess#test_signals_work_after_exec_fail [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_process.rb:2428]:
Expected #<Process::Status: pid 3252558 SIGABRT (signal 6) (core dumped)> to be success?.
  9) Failure:
TestProcess#test_threading_works_after_exec_fail [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_process.rb:2464]:
Expected #<Process::Status: pid 3252910 SIGABRT (signal 6) (core dumped)> to be success?.
 10) Failure:
TestProcess#test_process_detach [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_process.rb:2348]:
Expected #<Process::Status: pid 3253011 SIGABRT (signal 6) (core dumped)> to be success?.
 11) Failure:
TestThread#test_blocking_mutex_unlocked_on_fork [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_thread.rb:1223]:
[ruby-core:55102] [Bug #8433].
<false> expected but was
<nil>.
 12) Failure:
TestThread#test_fork_in_thread [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_thread.rb:1243]:
[ruby-core:62070] [Bug #9751]
pid 3253221 killed by SIGABRT (signal 6) (core dumped).
Expected #<Process::Status: pid 3253221 SIGABRT (signal 6) (core dumped)> to not be signaled?.
 13) Failure:
TestThread#test_fork_while_locked [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_thread.rb:1254]:
[ruby-core:85940] [Bug #14578].
Expected #<Process::Status: pid 3253241 SIGABRT (signal 6) (core dumped)> to be success?.
 14) Failure:
TestThread#test_fork_while_locked [/builddir/build/BUILD/ruby-3.1.0/tool/lib/zombie_hunter.rb:6]:
Expected [[3253249, #<Process::Status: pid 3253249 SIGABRT (signal 6) (core dumped)>],
 [3253250, #<Process::Status: pid 3253250 SIGABRT (signal 6) (core dumped)>]] to be empty.
Finished tests in 885.270988s, 24.2186 tests/s, 3106.1596 assertions/s.
21440 tests, 2749793 assertions, 14 failures, 0 errors, 56 skips
ruby -v: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux]
make: *** [uncommon.mk:822: yes-test-all] Aborted (core dumped)

Executing the test on itself, they passes just fine, but testing e.g. test/ruby/test_autoload.rb -v -n '/TestAutoload#test_autoload_fork/' together with test/fiddle/test_import.rb makes the reproducer smaller.

Trying to reduce the issue even further, I have reduced the test/ruby/test_import.rb into the following shape:

# coding: US-ASCII
# frozen_string_literal: true
begin
  require_relative 'helper'
  require 'fiddle/import'
rescue LoadError
end

module Fiddle
  module LIBC
    extend Importer
    dlload LIBC_SO, LIBM_SO

    CallCallback = bind("void call_callback(void*, void*)"){ | ptr1, ptr2|
#      f = Function.new(ptr1.to_i, [TYPE_VOIDP], TYPE_VOID)
#      f.call(ptr2)
    }
  end


end if defined?(Fiddle)

And use just miniruby:

$ gdb --args ./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems "./test/runner.rb" --ruby="./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems" --excludes-dir=./test/excludes --name='!/memory_leak/'  test/fiddle/test_import.rb test/ruby/test_autoload.rb -v -n '/TestAutoload#test_autoload_fork/'
GNU gdb (GDB) Fedora 11.1-6.fc36
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./miniruby...
warning: File "/builddir/build/BUILD/ruby-3.1.0/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
	add-auto-load-safe-path /builddir/build/BUILD/ruby-3.1.0/.gdbinit
line to your configuration file "/builddir/.config/gdb/gdbinit".
To completely disable this security protection add
	set auto-load safe-path /
line to your configuration file "/builddir/.config/gdb/gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
	info "(gdb)Auto-loading safe path"
(gdb) r
Starting program: /builddir/build/BUILD/ruby-3.1.0/miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems ./test/runner.rb --ruby=./miniruby\ -I./lib\ -I.\ -I.ext/common\ \ ./tool/runruby.rb\ --extout=.ext\ \ --\ --disable-gems --excludes-dir=./test/excludes --name=\!/memory_leak/ test/fiddle/test_import.rb test/ruby/test_autoload.rb -v -n /TestAutoload\#test_autoload_fork/
Download failed: No route to host.  Continuing without debug info for /builddir/build/BUILD/ruby-3.1.0/system-supplied DSO at 0x7ffff7fc4000.
Download failed: No route to host.  Continuing without debug info for /lib64/libz.so.1.
Download failed: No route to host.  Continuing without debug info for /lib64/libgmp.so.10.
Download failed: No route to host.  Continuing without debug info for /lib64/libcrypt.so.2.
Download failed: No route to host.  Continuing without debug info for /lib64/libm.so.6.
Download failed: No route to host.  Continuing without debug info for /lib64/libc.so.6.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
process 13364 is executing new program: /builddir/build/BUILD/ruby-3.1.0/ruby
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.34.9000-36.fc36.x86_64 gmp-6.2.1-1.fc36.x86_64 libxcrypt-4.4.27-1.fc36.x86_64 zlib-1.2.11-30.fc35.x86_64
Download failed: No route to host.  Continuing without debug info for /lib64/libz.so.1.
Download failed: No route to host.  Continuing without debug info for /lib64/libgmp.so.10.
Download failed: No route to host.  Continuing without debug info for /lib64/libcrypt.so.2.
Download failed: No route to host.  Continuing without debug info for /lib64/libm.so.6.
Download failed: No route to host.  Continuing without debug info for /lib64/libc.so.6.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Run options: 
  --seed=54837
  "--ruby=./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems"
  --excludes-dir=./test/excludes
  --name=!/memory_leak/
  -v
  -n
  /TestAutoload#test_autoload_fork/

# Running tests:

[Detaching after vfork from child process 13401]
[1/0] TestAutoload#test_autoload_fork[New Thread 0x7ffff4ccf640 (LWP 13402)]
[New Thread 0x7ffff4bae640 (LWP 13403)]
[New Thread 0x7ffff4a8d640 (LWP 13404)]
[New Thread 0x7ffff496c640 (LWP 13405)]
[New Thread 0x7ffff484b640 (LWP 13406)]
[New Thread 0x7ffff472a640 (LWP 13407)]
[Detaching after fork from child process 13408]
[Detaching after fork from child process 13409]
[Detaching after fork from child process 13410]
 = 0.39 s

  1) Failure:
TestAutoload#test_autoload_fork [/builddir/build/BUILD/ruby-3.1.0/test/ruby/test_autoload.rb:380]:
[ruby-core:86410] [Bug #14634].
Expected #<Test::Unit::AssertionFailedError: Expected #<Process::Status: pid 13409 SIGABRT (signal 6) (core dumped)> to be success?.> to be nil.

  2) Failure:
TestAutoload#test_autoload_fork [/builddir/build/BUILD/ruby-3.1.0/tool/lib/zombie_hunter.rb:6]:
Expected [[13410, #<Process::Status: pid 13410 SIGABRT (signal 6) (core dumped)>]] to be empty.

Finished tests in 0.392854s, 2.5455 tests/s, 12.7274 assertions/s.
1 tests, 5 assertions, 2 failures, 0 errors, 0 skips

ruby -v: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux]

Thread 1 "ruby" received signal SIGABRT, Aborted.
0x00007ffff78a764c in __pthread_kill_implementation () from /lib64/libc.so.6
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.34.9000-36.fc36.x86_64 gmp-6.2.1-1.fc36.x86_64 libxcrypt-4.4.27-1.fc36.x86_64 zlib-1.2.11-30.fc35.x86_64
(gdb) bt
#0  0x00007ffff78a764c in __pthread_kill_implementation () from /lib64/libc.so.6
#1  0x00007ffff785a656 in raise () from /lib64/libc.so.6
#2  0x00007ffff7844833 in abort () from /lib64/libc.so.6
#3  0x00007ffff4d0a5b8 in dlfree (mem=0x7ffff7bf1010) at ../src/dlmalloc.c:4350
#4  0x00007ffff4d190b1 in dealloc (ptr=0x5555558c1c00) at /builddir/build/BUILD/ruby-3.1.0/ext/fiddle/closure.c:32
#5  0x00007ffff7cb7801 in run_final (zombie=140737300557440, objspace=0x55555555d800) at /builddir/build/BUILD/ruby-3.1.0/gc.c:4011
#6  finalize_list (objspace=objspace@entry=0x55555555d800, zombie=140737300557440) at /builddir/build/BUILD/ruby-3.1.0/gc.c:4030
#7  0x00007ffff7cb80cc in rb_objspace_call_finalizer (objspace=0x55555555d800) at /builddir/build/BUILD/ruby-3.1.0/gc.c:4194
#8  0x00007ffff7ca56eb in rb_ec_finalize (ec=0x55555555dd70) at /builddir/build/BUILD/ruby-3.1.0/eval.c:164
#9  rb_ec_cleanup (ec=ec@entry=0x55555555dd70, ex0=<optimized out>) at /builddir/build/BUILD/ruby-3.1.0/eval.c:256
#10 0x00007ffff7ca5c14 in ruby_run_node (n=0x7ffff7699660) at /builddir/build/BUILD/ruby-3.1.0/eval.c:321
#11 0x000055555555518f in main (argc=<optimized out>, argv=<optimized out>) at ./main.c:47
(gdb) f 3
#3  0x00007ffff4d0a5b8 in dlfree (mem=0x7ffff7bf1010) at ../src/dlmalloc.c:4350
4350	      USAGE_ERROR_ACTION(fm, p);
(gdb) l
4345	          check_free_chunk(fm, p);
4346	          goto postaction;
4347	        }
4348	      }
4349	    erroraction:
4350	      USAGE_ERROR_ACTION(fm, p);
4351	    postaction:
4352	      POSTACTION(fm);
4353	    }
4354	  }

Raspberry Pi Cannot Update to 1.1.0

For some reason the Bullseye OS cannot update fiddle from 1.0.0 to 1.1.0 due to missing libraries (RP Zero 2 W, and RP4):

**mkmf.log:**
"pkg-config --exists libffi"
package configuration for libffi is not found

$ sudo apt install libffi7
[Wed Mar 30 07:57:00 rich@sniff ~] sudo apt install libffi7 -y
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
libffi7 is already the newest version (3.3-6).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Using bundler 2.3.10 and not finding any entry for fiddle in Gemfile.lock at all.

$ gem search fiddle

*** REMOTE GEMS ***

fiddle (1.1.0)
fiddle_fart (0.0.1)
fiddler (1.0.1)
fiddler-rb (0.1.2)
fiddlesticks (0.2.0)
fiddley (0.0.8)
jekyll-scalafiddle (1.0.1)
sfml-audio-fiddle (0.1.0)
sqlite3-fiddle (1.0.0)
wifiddler (0.0.1)

$  gem update fiddle
Updating installed gems
Updating fiddle
Building native extensions. This could take a while...
ERROR:  Error installing fiddle:
	ERROR: Failed to build gem native extension.

    current directory: /home/rich/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/fiddle-1.1.0/ext/fiddle
/home/rich/.rbenv/versions/2.7.1/bin/ruby -I /home/rich/.rbenv/versions/2.7.1/lib/ruby/2.7.0 -r ./siteconf20220330-11386-1jcr95p.rb extconf.rb
checking for --enable-debug-build option... no
checking for ffi.h... no
checking for ffi/ffi.h... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
	--with-opt-dir
	--without-opt-dir
	--with-opt-include
	--without-opt-include=${opt-dir}/include
	--with-opt-lib
	--without-opt-lib=${opt-dir}/lib
	--with-make-prog
	--without-make-prog
	--srcdir=.
	--curdir
	--ruby=/home/rich/.rbenv/versions/2.7.1/bin/$(RUBY_BASE_NAME)
	--enable-debug-build
	--disable-debug-build
	--enable-bundled-libffi
	--disable-bundled-libffi
	--with-libffi-dir
	--without-libffi-dir
	--with-libffi-include
	--without-libffi-include=${libffi-dir}/include
	--with-libffi-lib
	--without-libffi-lib=${libffi-dir}/lib
	--with-libffi-config
	--without-libffi-config
	--with-pkg-config
	--without-pkg-config
	--with-ffi-dir
	--without-ffi-dir
	--with-ffi-include
	--without-ffi-include=${ffi-dir}/include
	--with-ffi-lib
	--without-ffi-lib=${ffi-dir}/lib
extconf.rb:87:in `<main>': missing libffi. Please install libffi. (RuntimeError)

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /home/rich/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/extensions/armv7l-linux-eabihf/2.7.0/fiddle-1.1.0/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /home/rich/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/fiddle-1.1.0 for inspection.
Results logged to /home/rich/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/extensions/armv7l-linux-eabihf/2.7.0/fiddle-1.1.0/gem_make.out
Gems updated: fiddle

Error compiling on Fedora 33

gem update
Updating installed gems
Updating fiddle
Building native extensions. This could take a while...
ERROR:  Error installing fiddle:
	ERROR: Failed to build gem native extension.

    current directory: /home/steve/.gem/ruby/gems/fiddle-1.0.2/ext/fiddle
/usr/bin/ruby -I /usr/share/rubygems -r ./siteconf20201119-21950-1wwqy6q.rb extconf.rb
checking for ffi.h... yes
checking for -lffi... yes
libffi_version: 3.1.0
checking for ffi_prep_cif_var() in ffi.h... yes
checking for sys/mman.h... yes
checking for dlfcn.h... yes
checking for -ldl... yes
checking for dlopen()... yes
checking for dlclose()... yes
checking for dlsym()... yes
checking for dlerror()... yes
checking for FFI_STDCALL in ffi.h... yes
creating Makefile

current directory: /home/steve/.gem/ruby/gems/fiddle-1.0.2/ext/fiddle
make "DESTDIR=" clean
rm -f 
rm -f fiddle.so  *.o  *.bak mkmf.log .*.time

current directory: /home/steve/.gem/ruby/gems/fiddle-1.0.2/ext/fiddle
make "DESTDIR="
gcc -I. -I/usr/include -I/usr/include/ruby/backward -I/usr/include -I.  -I -DHAVE_FFI_H -DRUBY_LIBFFI_MODVERSION=3001000 -DHAVE_FFI_PREP_CIF_VAR -DHAVE_SYS_MMAN_H -DHAVE_DLFCN_H -DHAVE_DLOPEN -DHAVE_DLCLOSE -DHAVE_DLSYM -DHAVE_DLERROR -DHAVE_CONST_FFI_STDCALL    -fPIC -O2  -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fPIC  -m64 -o closure.o -c closure.c
gcc -I. -I/usr/include -I/usr/include/ruby/backward -I/usr/include -I.  -I -DHAVE_FFI_H -DRUBY_LIBFFI_MODVERSION=3001000 -DHAVE_FFI_PREP_CIF_VAR -DHAVE_SYS_MMAN_H -DHAVE_DLFCN_H -DHAVE_DLOPEN -DHAVE_DLCLOSE -DHAVE_DLSYM -DHAVE_DLERROR -DHAVE_CONST_FFI_STDCALL    -fPIC -O2  -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fPIC  -m64 -o conversions.o -c conversions.c
In file included from /usr/include/ruby.h:33,
                 from ./fiddle.h:4,
                 from conversions.c:1:
conversions.c: In function ‘rb_fiddle_type_ensure’:
./fiddle.h:130:25: error: ‘SIGNEDNESS_OF_SIZE_T’ undeclared (first use in this function); did you mean ‘SIGNEDNESS_OF_TIME_T’?
  130 | #define TYPE_SIZE_T (-1*SIGNEDNESS_OF_SIZE_T*TYPE_SSIZE_T)
      |                         ^~~~~~~~~~~~~~~~~~~~
conversions.c:89:28: note: in expansion of macro ‘TYPE_SIZE_T’
   89 |             return INT2NUM(TYPE_SIZE_T);
      |                            ^~~~~~~~~~~
./fiddle.h:130:25: note: each undeclared identifier is reported only once for each function it appears in
  130 | #define TYPE_SIZE_T (-1*SIGNEDNESS_OF_SIZE_T*TYPE_SSIZE_T)
      |                         ^~~~~~~~~~~~~~~~~~~~
conversions.c:89:28: note: in expansion of macro ‘TYPE_SIZE_T’
   89 |             return INT2NUM(TYPE_SIZE_T);
      |                            ^~~~~~~~~~~
make: *** [Makefile:246: conversions.o] Error 1

make failed, exit code 2

Gem files will remain installed in /home/steve/.gem/ruby/gems/fiddle-1.0.2 for inspection.
Results logged to /home/steve/.gem/ruby/extensions/x86_64-linux/2.7.0/fiddle-1.0.2/gem_make.out
Gems updated: fiddle

allow IO write to write memoryview object without copying or add IO#write_memoryview

as seen on rubybugs #17832 -

IO#write() - arguments that are not a string will be converted to string using to_s
want IO#write to be able to write memoryview objects without conversion to string

require` 'fiddle'
include Fiddle
ptr = Fiddle::Pointer['this is a string']
mv = MemoryView.new ptr
mv.byte_size  #17802 
mv[0]  # 116
'this is a string'.bytes[0] = 116
File.open('test.txt', 'wb'){ f.write mv}
contents = File.open('test.txt', 'r'){ |f| f.read}  # contents is  "#<Fiddle::MemoryView:0x000001a75ae76258>" not 'this is a string'

buffer = ByteBuffer.new('this is a string')
File.open('test.txt', 'w'){|f| f.write Fiddle::MemoryView.new(buffer)}

allow IO#write to write memoryview objects without converting to string or add a new method on IO to write memoryview objects without converting to string

struct implement memoryview

since c struct are contiguous in memory - have them implement the memoryview interface. That way we can use them with Fiddle::MemoryView

[Windows] RubyInstaller::Runtime#add_dll_directory raises a Segmental fault.

Hi fiddle developers!

Perhaps this issue is related to #48.

  • Windows
  • RubyInstaller
  • Ruby 2.7.2

tes.rb

require 'fiddle'
RubyInstaller::Runtime.add_dll_directory('C:\Users')
ruby tes.rb

Segmental fault occurs.

C:/Ruby27-x64/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/dll_directory.rb:75: [BUG] Segmentation fault
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x64-mingw32]

Interestingly, this error does not occur if you run the script using bundler.

Gemfile:

# frozen_string_literal: true

source "https://rubygems.org"

gem 'fiddle'
bundle install --path=vendor/bundle
bundle exec ruby tes.rb

Some warnings are raised, but Segmental faults are not raised. It seems to have been executed correctly.

C:/Users/kojix2/tmp/vendor/bundle/ruby/2.7.0/gems/fiddle-1.0.3/lib/fiddle.rb:55: warning: already initialized constant Fiddle::RTLD_GLOBAL
C:/Ruby27-x64/lib/ruby/2.7.0/fiddle.rb:53: warning: previous definition of RTLD_GLOBAL was here
C:/Users/kojix2/tmp/vendor/bundle/ruby/2.7.0/gems/fiddle-1.0.3/lib/fiddle.rb:56: warning: already initialized constant Fiddle::RTLD_LAZY
C:/Ruby27-x64/lib/ruby/2.7.0/fiddle.rb:54: warning: previous definition of RTLD_LAZY was here
C:/Users/kojix2/tmp/vendor/bundle/ruby/2.7.0/gems/fiddle-1.0.3/lib/fiddle.rb:57: warning: already initialized constant Fiddle::RTLD_NOW
C:/Ruby27-x64/lib/ruby/2.7.0/fiddle.rb:55: warning: previous definition of RTLD_NOW was here

Thank you.

Segfault when exiting due to `Fiddle::MemoryView`

The following script causes a segfault when exiting:

$workaround = false

require 'fiddle'

Fiddle::MemoryView.new(Fiddle::Pointer["foo"])

GC.start if $workaround

A backtrace from GDB:

Thread 1 received signal SIGSEGV, Segmentation fault.
rb_class_get_superclass (klass=klass@entry=0) at ../object.c:2252
2252        return RCLASS(klass)->super;
(gdb) bt
#0  rb_class_get_superclass (klass=klass@entry=0) at ../object.c:2252
#1  0x0000000589c03ae3 in lookup_memory_view_entry (klass=0) at ../memory_view.c:784
#2  0x0000000589c04921 in rb_memory_view_release (view=view@entry=0x800157770) at ../memory_view.c:836
#3  0x0000000596513c13 in fiddle_memview_free (ptr=0x800157770) at ../../../ext/fiddle/memory_view.c:41
#4  0x0000000589bd9226 in run_final (zombie=123145300985120, objspace=0x800052bc0) at ../gc.c:4040
#5  finalize_list (objspace=objspace@entry=0x800052bc0, zombie=123145300985120) at ../gc.c:4059
#6  0x0000000589bdd6e1 in rb_objspace_call_finalizer (objspace=0x800052bc0) at ../gc.c:4222
#7  0x0000000589bc5f37 in rb_ec_finalize (ec=ec@entry=0x800053520) at ../eval.c:187
#8  0x0000000589bc8e25 in rb_ec_cleanup (ec=ec@entry=0x800053520, ex=<optimized out>) at ../eval.c:298
#9  0x0000000589bc8fa8 in ruby_run_node (n=0x6fffffed3068) at ../eval.c:379
#10 0x00000001004017ad in main (argc=<optimized out>, argv=<optimized out>) at ../main.c:47

Invoking a GC before exiting seems to be a workaround.

ruby -v: ruby 3.1.0dev (2021-05-21T09:28:24Z master 50a534a152) [x86_64-linux]

Ruby Fiddle - Function behaves differently between C and Ruby

I am using Ruby Fiddle to access a C function to perform some heavy calculations. The C function works perfectly well when called directly, but when used via Fiddle it returns various rows of nans and infs in an unpredictable way. The function operates on matrices that are passed as pointers to arrays.

I have debugged the C code and everything works fine. I have also saved the various parameters passed to the C function to file to be sure that Fiddle was not passing some weird values, but there is no obvious (at least for me) problem.

Additionally it seems that for 'smaller' matrices this does not happen.
Apologies in advance for the code being very long, but this is the only way to exactly reproduce what it is happening. Data for testing is in this file. You can just copy and paste the Ruby and C test data to the code below.
I am working on macos Catalina and Ruby 2.2.4 (requirement).

The C code is the following:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

double *mrt(
    double *person_total_shortwave,
    int rows_pts, int cols_pts,
    double *dry_bulb_temperatures,
    double *ground_temperatures,
    double *atmospheric_longwave,
    double *sky_view_factors,
    double *shading_view_factors_matrix,
    int rows_svf, int cols_svf,
    double *shading_temperatures_matrix,
    int rows_st, int cols_st,
    int size_dbt,
    double person_emissivity_shortwave, double person_emissivity_longwave,
    double surroundings_emissivity, double ground_emissivity, double ground_person_view_factor);

int save_to_file(char *filename, int m, int n, double *mat)
{
    FILE *fp;
    int i, j;

    if ((fp = freopen(filename, "w", stdout)) == NULL)
    {
        printf("Cannot open file.\n");
        exit(1);
    }
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            if (j == n - 1)
                printf("%.17g \n", mat[i * n + j]);
            else
                printf("%.17g ", mat[i * n + j]);
    fclose(fp);
    return 0;
}

int main(void)
{

    // REPLACE WITH C DATA FROM FILE

    double *mrt_result;
    mrt_result = mrt(person_total_shortwave[0],
                     rows_pts, cols_pts,
                     dry_bulb_temperatures,
                     ground_temperatures,
                     atmospheric_longwave,
                     sky_view_factors,
                     shading_view_factors_matrix[0],
                     rows_svf, cols_svf,
                     shading_temperatures_matrix[0],
                     rows_st, cols_st,
                     size_dbt,
                     person_emissivity_shortwave, person_emissivity_longwave,
                     surroundings_emissivity, ground_emissivity, ground_person_view_factor);

    // save_to_file(rows_pts, cols_pts, mrt_result);

    return 0;
}
// https://www.dropbox.com/s/ix06edrrctad421/Calculation%20of%20Mean%20Radiant%20Temperature3.odt?dl=0

double *mrt(
    double *person_total_shortwave,
    int rows_pts, int cols_pts,
    double *dry_bulb_temperatures,
    double *ground_temperatures,
    double *atmospheric_longwave,
    double *sky_view_factors,
    double *shading_view_factors_matrix,
    int rows_svf, int cols_svf,
    double *shading_temperatures_matrix,
    int rows_st, int cols_st,
    int size_dbt,
    double person_emissivity_shortwave, double person_emissivity_longwave,
    double surroundings_emissivity, double ground_emissivity, double ground_person_view_factor)
{
    save_to_file("PTS.txt", rows_pts, cols_pts, person_total_shortwave);
    save_to_file("SVF.txt", 1, cols_svf, sky_view_factors);
    save_to_file("SVSM.txt", rows_svf, cols_svf, shading_view_factors_matrix);
    save_to_file("STM.txt", rows_st, cols_st, shading_temperatures_matrix);

    double *mrt_mat = (double *)calloc(rows_pts * cols_pts, sizeof(double));
    save_to_file("MRT_MAT0.txt", rows_pts, cols_pts, mrt_mat);

    int t, c, k;
    double sigma = 5.67E-8;
    double body_area = 1.94;

    double tmrt4_shortwave, tmrt4_longwave_ground, tmrt4_atm_longwave, tmrt4_surroundings;
    double tmrt4_shading[rows_svf];
    memset(tmrt4_shading, 0.0, rows_svf * sizeof(double));
    double tmrt4;
    double surroundings_view_factor;

    // t runs through the timesteps
    // c runs through the points in the mesh
    for (t = 0; t < rows_pts; t++)
    {
        for (c = 0; c < cols_pts; c++)
        {
            tmrt4_shortwave = 1.0 / (sigma * body_area) * (person_emissivity_shortwave / person_emissivity_longwave) * person_total_shortwave[t * cols_pts + c];

            // We are assuming that the ground is at ambient temperature
            tmrt4_longwave_ground = ground_person_view_factor * ground_emissivity * pow((273.15 + ground_temperatures[t]), 4);

            // Here we are using the actual long wave radiation from the sky
            tmrt4_atm_longwave = (1.0 - ground_person_view_factor) / sigma * sky_view_factors[c] * atmospheric_longwave[t];

            // We need to remove the contribution of all the shading devices
            // k runs through the shading devices
            surroundings_view_factor = 1.0 - sky_view_factors[c];
            for (k = 0; k < rows_svf; k++)
            {
                surroundings_view_factor -= shading_view_factors_matrix[k * cols_svf + c];
            }
            tmrt4_surroundings = (1.0 - ground_person_view_factor) * surroundings_view_factor * surroundings_emissivity * pow((273.15 + dry_bulb_temperatures[t] - 0.0), 4);

            // We now need to account for all contributions of the shading devices
            for (k = 0; k < rows_svf; k++)
            {
                tmrt4_shading[k] = (1.0 - ground_person_view_factor) * (shading_view_factors_matrix[k * cols_svf + c]) * surroundings_emissivity * pow((273.15 + shading_temperatures_matrix[k * cols_svf + t]), 4);
            }

            // Finally we add them all (see paper) for the total contribution
            tmrt4 = tmrt4_shortwave + tmrt4_longwave_ground + tmrt4_atm_longwave + tmrt4_surroundings;
            for (k = 0; k < rows_svf; k++)
            {
                tmrt4 += tmrt4_shading[k];
            }

            // Just convert to celsius
            mrt_mat[t * cols_pts + c] = pow(tmrt4, 0.25) - 273.15;
        }
    }
    save_to_file("MRT_MAT.txt", rows_pts, cols_pts, mrt_mat);

    // double x = 1.5;
    // while (1)
    // {
    //     x *= sin(x) / atan(x) * tanh(x) * sqrt(x);
    // }
    return mrt_mat;
}

and I compile is with clang -g --extra-warnings utils.c -o utils.

The Ruby code is the following

require "fiddle"
require "fiddle/import"

module RG
  extend Fiddle::Importer
  @handler.handlers.each { |h| h.close unless h.close_enabled? } unless @handler.nil?
  GC.start

  dlload File.join("utils")
  extern "double* mrt(double*, int, int, double*, double*, double*, double*, double*, int, int, double*, int, int, int, double, double, double, double, double)"

  def self.mat_to_ptr(matrix)
    return Fiddle::Pointer[matrix.flatten.pack("E*")]
  end

  def self.ptr_to_mat(ptr, rows, cols)
    length = rows * cols * Fiddle::SIZEOF_DOUBLE
    mat = ptr[0, length]
    return mat.unpack("E*").each_slice(cols).to_a
  end

  def self.mean_radiant_temperature(
    person_total_shortwave,
    dry_bulb_temperatures,
    ground_temperatures,
    atmospheric_longwave,
    sky_view_factors,
    shading_view_factors_matrix,
    shading_temperatures_matrix,
    person_emissivity_shortwave,
    person_emissivity_longwave,
    surroundings_emissivity,
    ground_emissivity,
    ground_person_view_factor
  )
    rows_pts = person_total_shortwave.size
    cols_pts = person_total_shortwave[0].size
    person_total_shortwave_pointer = RG.mat_to_ptr(person_total_shortwave)

    dry_bulb_temperatures_pointer = RG.mat_to_ptr(dry_bulb_temperatures)
    ground_temperatures_pointer = RG.mat_to_ptr(ground_temperatures)
    size_dbt = dry_bulb_temperatures.size

    atmospheric_longwave_pointer = RG.mat_to_ptr(atmospheric_longwave)
    sky_view_factors_pointer = RG.mat_to_ptr(sky_view_factors)

    rows_svf = shading_view_factors_matrix.size
    if rows_svf > 0
      cols_svf = shading_view_factors_matrix[0].size
    else
      cols_svf = 0
    end

    shading_view_factors_matrix_pointer = RG.mat_to_ptr(shading_view_factors_matrix)

    rows_st = shading_temperatures_matrix.size
    if rows_st > 0
      cols_st = shading_temperatures_matrix[0].size
    else
      cols_st = 0
    end

    shading_temperatures_matrix_pointer = RG.mat_to_ptr(shading_temperatures_matrix)

    mrt_pointer = mrt(
      person_total_shortwave_pointer,
      rows_pts, cols_pts,
      dry_bulb_temperatures_pointer,
      ground_temperatures_pointer,
      atmospheric_longwave_pointer,
      sky_view_factors_pointer,
      shading_view_factors_matrix_pointer,
      rows_svf, cols_svf,
      shading_temperatures_matrix_pointer,
      rows_st, cols_st,
      size_dbt,
      person_emissivity_shortwave,
      person_emissivity_longwave,
      surroundings_emissivity,
      ground_emissivity,
      ground_person_view_factor
    )
    return RG.ptr_to_mat(mrt_pointer, rows_pts, cols_pts)
  end
end

// REPLACE WITH RUBY DATA FROM FILE

mean_radiant_temperatures = RG.mean_radiant_temperature(
  person_total_shortwave,
  dry_bulb_temperatures,
  ground_temperatures,
  atmospheric_longwave,
  sky_view_factors,
  shading_view_factors_matrix,
  shading_temperatures_matrix,
  person_emissivity_shortwave,
  person_emissivity_longwave,
  surroundings_emissivity,
  ground_emissivity,
  ground_person_view_factor
)
File.open("MRT_MAT_RUBY.txt", "w") do |f|
  mean_radiant_temperatures.each do |row|
    f.puts row.join(' ')
  end
end

If you want to test it, first launch ./utils. It will save a few files on the local folder. Have a look at MRT_MAT.txt.

Now launch the ruby code several times. It will generate the same file, but you will notice that "often" the file will contain random rows with nan and inf. The same data is returned to Ruby and saved in the file MRT_MAT_RUBY.txt in the local directory.

I am quite familiar with Ruby, but C is not really my strength. It would be great being able to debug the C code when called from Ruby, but I don't really know how to do it.

Invalid handle usage - Application Verifier on Windows

When running Ruby on Windows under Application Verifier (the Windows SDK dynamic verification toolset) with invalid handle checking enabled, ruby immediately triggers on Init_fiddle when trying to run irb. If this is a problem somewhere else in ruby, let me know! I'm new to ruby.

App verifier log:
ruby.exe.12.dat.zip

Windbg interactive stacktrace:

0:000> kc
 # Call Site
00 ntdll!KiRaiseUserExceptionDispatcher
01 vfbasics!AVrfpNtDeviceIoControlFile
02 KERNELBASE!ConsoleCallServerGeneric
03 KERNELBASE!ConsoleCallServer
04 KERNELBASE!GetConsoleScreenBufferInfoEx
05 KERNELBASE!GetConsoleScreenBufferInfo
06 libffi_6!ffi_call_win64
07 libffi_6!ffi_call
08 fiddle!Init_fiddle
09 x64_msvcrt_ruby260!rb_thread_call_without_gvl
0a fiddle!Init_fiddle
0b x64_msvcrt_ruby260!rb_error_arity
0c x64_msvcrt_ruby260!rb_vm_invoke_bmethod
0d x64_msvcrt_ruby260!rb_vm_invoke_bmethod
0e x64_msvcrt_ruby260!rb_check_funcall
0f x64_msvcrt_ruby260!rb_vm_exec
10 x64_msvcrt_ruby260!rb_check_funcall_with_hook
11 x64_msvcrt_ruby260!rb_rescue2
12 x64_msvcrt_ruby260!rb_f_notimplement
13 x64_msvcrt_ruby260!rb_error_arity
14 x64_msvcrt_ruby260!rb_vm_invoke_bmethod
15 x64_msvcrt_ruby260!rb_vm_invoke_bmethod
16 x64_msvcrt_ruby260!rb_check_funcall
17 x64_msvcrt_ruby260!rb_vm_exec
18 x64_msvcrt_ruby260!rb_yield_1
19 x64_msvcrt_ruby260!rb_check_block_call
1a x64_msvcrt_ruby260!rb_catch
1b x64_msvcrt_ruby260!rb_error_arity
1c x64_msvcrt_ruby260!rb_vm_invoke_bmethod
1d x64_msvcrt_ruby260!rb_vm_invoke_bmethod
1e x64_msvcrt_ruby260!rb_check_funcall
1f x64_msvcrt_ruby260!rb_vm_exec
20 x64_msvcrt_ruby260!rb_yield_1
21 x64_msvcrt_ruby260!rb_check_block_call
22 x64_msvcrt_ruby260!rb_catch
23 x64_msvcrt_ruby260!rb_error_arity
24 x64_msvcrt_ruby260!rb_vm_invoke_bmethod
25 x64_msvcrt_ruby260!rb_vm_invoke_bmethod
26 x64_msvcrt_ruby260!rb_check_funcall
27 x64_msvcrt_ruby260!rb_vm_exec
28 x64_msvcrt_ruby260!rb_iseqw_local_variables
29 x64_msvcrt_ruby260!rb_iseqw_local_variables
2a x64_msvcrt_ruby260!rb_error_arity
2b x64_msvcrt_ruby260!rb_vm_invoke_bmethod
2c x64_msvcrt_ruby260!rb_vm_invoke_bmethod
2d x64_msvcrt_ruby260!rb_check_funcall
2e x64_msvcrt_ruby260!rb_vm_exec
2f x64_msvcrt_ruby260!rb_call_end_proc
30 x64_msvcrt_ruby260!ruby_run_node
31 image00000000_00400000
32 image00000000_00400000
33 image00000000_00400000
34 KERNEL32!BaseThreadInitThunk
35 ntdll!RtlUserThreadStart

Looking in windbg, it looks like the handle was previously closed:

Handle = 0x00000000000004bc - CLOSE
Thread ID = 0x0000000000003d18, Process ID = 0x000000000000401c

0x00007ff98d90ab84: ntdll!NtClose+0x0000000000000014
0x00007ff983613040: vfbasics!AVrfpNtClose+0x0000000000000040
0x00007ff98a595012: KERNELBASE!CloseHandle+0x0000000000000062
0x00007ff98361512d: vfbasics!AVrfpCloseHandleCommon+0x00000000000000a1
0x00007ff9836151cd: vfbasics!AVrfpKernelbaseCloseHandle+0x000000000000001d
0x00007ff98d7ba98a: msvcrt!close_nolock+0x0000000000000062
0x00007ff98d7bb1de: msvcrt!_dup2_nolock+0x000000000000008e
0x00007ff98d7bb103: msvcrt!dup2+0x0000000000000133
0x000000006a668370: x64_msvcrt_ruby260!rb_w32_dup2+0x0000000000000010
0x000000006a57f868: x64_msvcrt_ruby260!rb_execarg_run_options+0x0000000000000628
0x000000006a507744: x64_msvcrt_ruby260!rb_file_open+0x0000000000002aa4
0x000000006a5091b3: x64_msvcrt_ruby260!rb_file_open+0x0000000000004513
0x000000006a6338d6: x64_msvcrt_ruby260!rb_error_arity+0x0000000000000136
0x000000006a63cd3e: x64_msvcrt_ruby260!rb_vm_invoke_bmethod+0x000000000000048e
0x000000006a63d433: x64_msvcrt_ruby260!rb_vm_invoke_bmethod+0x0000000000000b83

Full debug session text:
ruby_invalid_handle_crash.TXT

parse_ctype() fails for "int long len"

parse_ctype() does not handle int long len correctly; errors with unknown type: int

[22] pry(#<Fiddle::CastXMLImporter::Importer>)> parse_struct_signature(["char id", "int long size"])
Fiddle::DLError: unknown type: int
from /Users/dmlary/.rbenv/versions/3.0.2/lib/ruby/3.0.0/fiddle/cparser.rb:245:in `parse_ctype'
[24] pry(#<Fiddle::CastXMLImporter::Importer>)> parse_ctype("int long size")
Fiddle::DLError: unknown type: int
from /Users/dmlary/.rbenv/versions/3.0.2/lib/ruby/3.0.0/fiddle/cparser.rb:245:in `parse_ctype'
[25] pry(#<Fiddle::CastXMLImporter::Importer>)> parse_ctype("int long")
=> 4

I am aware that I can switch the field definition to long, but these types are being pulled out of CastXML, and I'd prefer to fix the bug in Fiddle instead of implementing a bunch of type remapping.

old

parse_struct_signature() is passing the complete struct member definition to parse_ctype(). This includes both the ctype and the member name. This causes a problem when handling structs that contain compound members such as: int long length in their definition.

NetWkstaGetInfo() always fails with INVALID PARAMETER

Environment

  • Windows Server 2019
  • Ruby 2.7.5p203 (2021-11-24 revision f69aeb8314)

How to reproduce

Run the following script on Windows:

require "fiddle/import"
require "fiddle/types"
require "fiddle/import"

module M
  extend Fiddle::Importer
  dlload "netapi32.dll"
  include Fiddle::Win32Types
  extern "int NetWkstaGetInfo(void*, int, void*);"
  # NOTE! If we remove "#" from the following line, it works somehow.
  #extern "int NetWkstaUserGetInfo(void*, int, void*)"
end

buf = "\0" * Fiddle::SIZEOF_VOIDP
pbuf = Fiddle::Pointer[buf]
p M.NetWkstaGetInfo(nil, 102, pbuf)

Bug/Observe

  • NetwkstaGetInfo() always fails with error code 87 (= "invalid parameter").
    • If we enable extern int NetWkstaUserGetInfo(...) (see line 10-11), it starts working somehow.
  • I suspect that there is a subtle bug in FIddle that messes up the function call.

Expected behavior

  • NetWkstaGetInfo() exits with sucecss (= 0) and returns machine info correctly.

Fiddle::Closure::BlockCallerの記述が長い

rurema/doctree#2406 に関連して

Fiddle::Closure::BlockCallerが長くて書くのが辛いかなと思いました。

changed_callback = Fiddle::Closure::BlockCaller.new(FIddle::TYPE_VOID, [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP]) do |ptr1, ptr2|
  fuga_fuga_hoge
  0
end

みたいな記述を毎回書くのがちょっと大変だと感じます。

Fiddle::Closure::BlockCallerは、Fiddle::Closureを継承しています。
なのでFiddle::Closureの内部よりは横にあった方が、つまり Fiddle::BlockCaller などが自然かなとも思いました。

Rubyの公式リファレンスにそんな感じの誤記(Fiddle::BlockClosure)を見つけたのですが、この誤記をみて、そっちの方が自然だなと思いました。
Fiddle、いまさら仕様は変更しにくいと思います。
書くかどうか迷いましたが、単なる一つの意見として。

Question: How to allocate an array of structs?

Hi!

How to allocate an array of structs?
I tried the following code, but it does not work as expected. It may be because the same area is released twice.

require 'fiddle/import'

A = Fiddle::Importer.struct [
  'int x',
  'int y'
]

as = Fiddle::Pointer.malloc(A.size * 3)

structs = Array.new(3){ |i|
  size = A.size
  A.new(as.to_i + i * size, size)
}

Thank you.

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.