Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use Fiddle to pass or return a struct to native code?

Tags:

I would like to use Fiddle to access a native library compiled from Rust code. The C representation of the struct is very simple, it is just a pointer and a length:

typedef struct {
    char *data;
    size_t len;
} my_thing_t;

// Example function that somehow accepts a struct
void accepts_a_struct(my_thing_t thing);

// Example function that somehow returns a struct
my_thing_t returns_a_struct(void);

However, all examples I can find accept or return pointers to structs, and not the structs themselves. I'd like to avoid the double indirection if at all possible.

I've borrowed an example from the Fiddle::Importer documentation. However, I do not see how to properly call the extern method with a structure instead of a pointer to a structure:

require 'fiddle'
require 'fiddle/import'

module LibSum
  extend Fiddle::Importer
  dlload './libsum.so'
  extern 'double sum(double*, int)'
  extern 'double split(double)'
end

Note

Fiddle is not the same as the FFI gem. Fiddle is a component of the Ruby standard library, and is not provided as a separate gem. These related questions refer to the FFI gem, and not to Fiddle:

  • How to wrap function in Ruby FFI method that takes struct as argument?
  • How do I specify a struct as the return value of a function in RubyFFI?
like image 285
Shepmaster Avatar asked May 18 '15 00:05

Shepmaster


1 Answers

I've gone through Fiddle documentation and as I can see it is not possible since even in core function definition Fiddle::Function.new it requires args that Fiddle::CParser can handle. I've done various test and to make it work I had to transform your code into something like this:

test2.c

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

typedef struct {
  char *data;
  char *more_data;
  size_t len;
} my_thing_t;

my_thing_t *returns_a_struct(void){
  my_thing_t *structure = malloc(sizeof(my_thing_t));
  structure->data = "test2";
  structure->more_data = "I am more data";
  structure->len = 5;
  return structure;
};

irb

require 'fiddle'
require 'fiddle/import'
module Testmd
  extend Fiddle::Importer
  dlload './test2.dll'
  RetStruct = struct ['char *data','char *more_data','size_t len']
  extern 'RetStruct* returns_a_struct(void)'
end
include Testmd
2.2.1 :013 >   res = Testmd::returns_a_struct(nil)
 => #<Fiddle::Pointer:0x00000000b12a10 ptr=0x00000000e066b0 size=0 free=0x00000000000000> 
2.2.1 :014 > s = RetStruct.new(res)
 => #<Testmd::RetStruct:0x00000000c3e9e8 @entity=#<Fiddle::CStructEntity:0x000000007f0ad0 ptr=0x00000000e066b0 size=24 free=0x00000000000000>> 
2.2.1 :015 > s.data.to_s
 => "test2" 
2.2.1 :016 > s.more_data.to_s
 => "I am more data" 
2.2.1 :017 > s.len
 => 5

What I came to is that Fiddle can operate with simple types but needs struct and union types to be passed using references. Still it has wrappers for this classes. Also these wrappers are inherited from Fiddle::Pointer what kinda leads us to conclusion they want us to use pointers for these data types.

If you want more details regarding this or you want to add this functionality you can go through their git repo.

like image 187
Glupo Avatar answered Oct 01 '22 09:10

Glupo