Search code examples
rubyinline-code

Compiling Ruby Inline C code - resolving errors


I am trying to get this Ruby inline C code http://pastie.org/2825882 to work. The code works in vanilla C, but here I get errors and warnings. What causes this error?

./backtrack_inline.rb:67: error: lvalue required as unary '&' operand

Also, why do I get the following error?

./backtrack_inline.rb:73: error: too few arguments to function 'backtrack'

Inspecting the resulting C code ( http://pastie.org/2826036) I fail to see anything wrong with the arguments. But I do also get the following warnings:

./backtrack_inline.rb:73: warning: passing argument 1 of 'backtrack' makes integer from pointer without a cast
./backtrack_inline.rb:73: warning: passing argument 2 of 'backtrack' makes integer from pointer without a cast
./backtrack_inline.rb:73: warning: passing argument 3 of 'backtrack' makes integer from pointer without a cast

Solution

  • Starting with this:

    ./backtrack_inline.rb:73: error: too few arguments to function 'backtrack'
    

    If you look at your generated code, the backtrack function is defined on line 29:

    static VALUE backtrack(VALUE self, VALUE _ss, VALUE _s, VALUE _p, VALUE _mm, VALUE _ins, VALUE _del) { ... }
    

    It has seven arguments, the original six, plus VALUE self as it has been converted into a method on the Scan class.

    The call to this function, on line 67 looks like this:

    end = backtrack(ss, s, p, mm, ins, del);
    

    It has only six arguments. RubyInline doesn't convert this to a call to a method on the object, it simply copies it verbatim. This is also where the warnings about makes integer from pointer without a cast come from: the function definition has been converted to take VALUEs, but you're calling with the original types.

    The error message says that the error is from line 73 in backtrack_inline.rb because of the directive on line 54 of the generated code:

    # line 61 "./backtrack_inline.rb"
    

    which basically tells the compiler to "reset" its line and file values for errors, and treat the next line (55) as being line 61 in the file ./backtrack_inline.rb. The actual line is 67, 12 ahead of 55, but the compiler reports it as being 73, 12 ahead of 61 (the value it was reset to) and from a differnt file. This technique doesn't really work in this case as it doesn't take into account the extra lines added by RubyInline. The actual line in the source is 69.

    A simple fix for this is to change the definition of the backtrack function to be just a C function rather than add it as a method on the object. Change builder.c to builder.prefix (on line 38 of your Ruby file). This won't work if you want to have backtrack available as a method on the object in Ruby. If that's the case you might need create another function to be the method, which then wraps the "real" backtrack function.

    Next, looking at

    ./backtrack_inline.rb:67: error: lvalue required as unary '&' operand
    

    This actually refers to line 61 of the generated code, which looks like:

    char* s = StringValuePtr(rb_iv_get(self, "@seq"));
    

    StringValuePtr is a macro which is defined as:

    #define StringValue(v) rb_string_value(&(v))
    

    This is where the & in lvalue required as unary '&' operand comes from. You need to add a local variable to be the lvalue:

    VALUE seq = rb_iv_get(self, "@seq");
    char* s = StringValuePtr(seq);
    

    In my case (Mac OS X Snow Leopard, Ruby 1.9.3-p0, RubyInline 3.11.0) these two changes made the script run without errors, but gave the warning:

    backtrack_inline.rb:47: warning: implicit conversion shortens 64-bit value into a 32-bit value
    

    This actually refers to line 46 of the ruby file:

    return (s - ss) - 1;
    

    s and ss are char *, i.e. 64 bit pointers (on this machine), and the return type of the function is int - 32 bits. Adding an explicit cast fixed this:

    return (int)((s - ss) - 1);
    

    It now runs cleanly:

    ruby-inline $ ruby backtrack_inline.rb 
    14
    ruby-inline $ 
    

    (I hope 14 is the correct answer!)

    Here's a version of the script with these changes.