4

I am trying to define the Tinn C lib in Ruby using fiddle, but it is giving me an error with the struct

Tinn.h

typedef struct
{
    // All the weights.
    float* w;
    // Hidden to output layer weights.
    float* x;
    // Biases.
    float* b;
    // Hidden layer.
    float* h;
    // Output layer.
    float* o;
    // Number of biases - always two - Tinn only supports a single hidden layer.
    int nb;
    // Number of weights.
    int nw;
    // Number of inputs.
    int nips;
    // Number of hidden neurons.
    int nhid;
    // Number of outputs.
    int nops;
}
Tinn;

float* xtpredict(Tinn, const float* in);

float xttrain(Tinn, const float* in, const float* tg, float rate);

Tinn xtbuild(int nips, int nhid, int nops);

Ruby fiddle

module Tinn
    extend Fiddle::Importer
    dlload './tinn.so'

    Tinn = struct [
        # All the weights.
        'float* w',
        # Hidden to output layer weights.
        'float* x',
        # Biases.
        'float* b',
        # Hidden layer.
        'float* h',
        # Output layer.
        'float* o',
        # Number of biases - always two - Tinn only supports a single hidden layer.
        'int nb',
        # Number of weights.
        'int nw',
        # Number of inputs.
        'int nips',
        # Number of hidden neurons.
        'int nhid',
        # Number of outputs.
        'int nops'
    ]

    extern 'float* xtpredict(Tinn, const float* in)'

    extern 'float xttrain(Tinn, const float* in, const float* tg, float rate)'

    extern 'Tinn xtbuild(int nips, int nhid, int nops)'
end

I am gettting an error as such

/home/arjun/.rbenv/versions/2.3.3/lib/ruby/2.3.0/fiddle/cparser.rb:177:in `parse_ctype': unknown type: Tinn (Fiddle::DLError)
    from /home/arjun/.rbenv/versions/2.3.3/lib/ruby/2.3.0/fiddle/cparser.rb:90:in `block in parse_signature'
    from /home/arjun/.rbenv/versions/2.3.3/lib/ruby/2.3.0/fiddle/cparser.rb:90:in `collect'
    from /home/arjun/.rbenv/versions/2.3.3/lib/ruby/2.3.0/fiddle/cparser.rb:90:in `parse_signature'
    from /home/arjun/.rbenv/versions/2.3.3/lib/ruby/2.3.0/fiddle/import.rb:163:in `extern'
    from rb_tinn.rb:31:in `<module:Tinn>'
    from rb_tinn.rb:4:in `<main>'

Line 31 points to the first function where we pass struct as an argument float* xtpredict(Tinn, const float* in)

I have already defined Tinn as a struct, but still it is giving the error.

3
  • 1
    Fiddle is not able to interface with functions that pass structs. Only struct pointers. See here: stackoverflow.com/a/31135028/823617 Commented Jun 20, 2018 at 4:04
  • So I need to wrap it into a function? Separately, what about FFI, I did try that as well but I started getting segmentation faults. Commented Jun 20, 2018 at 9:52
  • I would wrap it into a C-function that accepts a struct pointer. Then interface to that one with Fiddle. FFI is very hard to wrap your head around. It's probably possible to do it with FFI, but it's not very well documented so it might be hard to make it work properly. Commented Jun 20, 2018 at 14:14

1 Answer 1

3
+50

Structs with Fiddle in Ruby are slightly misleading as being a direct analog of its C counterpart. Many of the features that make structs easy and user-friendly are lacking within its Ruby representation.

The main principle to keep in mind is that a Fiddle::CStruct is really nothing more than glorified Fiddle::Pointer, and is in fact a subclass of it. The main limitations are that you can only declare primitive types within it. Unlike in C languages where if you define a struct, you can then use it as a type within another struct, a return value, etc.

This can still be accomplished in Ruby, though the documentation is light on the issue. As I stated previously, bearing in mind that a Ruby struct derives from Fiddle::Pointer, you simply use void* in your signature declaration (or Fiddle::TYPE_VOIDP depending on whether or not you are yusing the CParser).

In your above example, once you have declared your struct, you would then define your method signature like this:

float* xtpredict(void* tinn, const float* in)

Replace your struct types with void* instead of the name of the type, and you can then pass the struct directly to it and get the desired behavior you would expect.

The same way goes for getting a struct returned from a method. Once again use the void* in the signature, which Ruby will then return a Fiddle::Pointer object to. This pointer contains the address to where the struct is in memory. Since structs take an address in their initialize method, use the returned pointer to initialize your struct, and it will be created at that location in memory.

ptr = getMyStruct
MyStruct.new(ptr.to_i)

Conversely, if memory serves me correct, I believe you can also use it like this:

ptr = getMyStruct
MyStruct.new(ptr.ptr)
Sign up to request clarification or add additional context in comments.

12 Comments

That works! I have to still define a separate function to accommodate xtbuild which returns a struct.
It is the same concept, define the return as void*, and initialize your struct with that address. It will return Fiddle::Pointer, which you use as the pointer to your struct. It is not as intuitive as using a C language, but it does work out okay once you get used to the differences. You basically need to reduce every "type" to a primitive or a pointer to a primitive. A "struct" in Ruby is just a fancy pointer, that offers some additional functionality for named values, takes care of the offsets for you, etc.
I expanded upon my original answer to clarify some things.
I think I got an idea how to progress now. I had already made the changes for xtpredict as soon as you posted, I was having doubts on xtbuild, the last function. Let me check. Appreciate your help, though.
This is completely WRONG. See this answer. They are not using the FFI_TYPE_STRUCT at all in the conversion.c, which is required as can be seen at this.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.