Creating C Structures Using RubyInline Gem

I have a Rails application that should show the results of the Monte Carlo simulator, and for the first time, Ruby is simply not fast enough for my needs. So, I started digging around to see if I could rewrite my simulation in C and use these results in Ruby, and some googling turned RubyInline gem to easily write faster C code directly in Ruby. Simple things work fine, for example, some basic functions written in both Ruby and C:

class FasterFunctions inline do |builder| builder.c ' double rand_sum(int trials) { double sum = 0.0; for (int i = 0; i<trials; i++) { sum += (double)rand()/(double)RAND_MAX; } return sum; }' builder.c ' long loop_sum(long trials) { long sum = 0; for (long i = 0; i<trials; i++) { sum+=i; } return sum; }' end end #the C version is 4 orders of magnitude faster trials = 1_000_000 ruby_sum = 0 trials.times {|i| ruby_sum += i} c_sum = FasterRand.new.loop_sum(trials) # the C version is 1 order of magnitude faster ruby_sum = 0.0 trials.times {ruby_sum += rand} c_sum = FasterRand.new.rand_sum(trials) 

So great, this will definitely speed up my sim, as it pretty much just generates random numbers and adds stuff based on these results. Unfortunately, besides basic things like this, I can't figure out how to write my program. I need to pass some structures in order to act as state variables, so the first order of business is figuring out how to create a C structure. Reading the documentation seems to be pretty simple.

accessor (method, type, member = method)

Adds a reader and writer for a C structure element wrapped through Data_Wrap_Struct. method is the name of the ruby ​​that the accessory gives, type is type C. If member name C is not redefined by member, the method name is used as a member of the structure.

 builder.struct_name = 'MyStruct' builder.accessor :title, 'char *' builder.accessor :stream_index, 'int', :index 

I don’t quite understand the documentation, but I assume that I put it in a class similar to the previous one and do something like the following to access it:

 class MyStructClass inline do |builder| builder.struct_name = 'MyStruct' builder.accessor :title, 'char *' builder.accessor :stream_index, 'int', :index end end #possible this struct = MyStructClass.new.MyStruct struct.title = 'A Title' #or maybe struct = MyStructClass.new struct.title = 'A Title' 

Sorry, I can't even go this far

.

* / rbenv / version / 2.1.2 / Library / ruby ​​/ stones / 2.1.0 / gems / RubyInline -3.12.4 / Library / inline.rb: 456: 3: Error: using an undeclared identifier Pointer MyStruct MyStruct *; ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 456: 13: error: using an undeclared identifier pointer "pointer" MyStruct *; ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 458: 35: error: use of undeclared identifier 'pointer' Data_Get_Struct (self, MyStruct, pointer); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/ruby.h:1038:6: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj); \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 458: 25: error: using the undeclared identifier 'MyStruct' Data_Get_Struct (self, MyStruct, pointer); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/ruby.h:1038:15: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj); \ ^. * / Rbenv / version / 2.1.2 / Library / ruby ​​/ stones / 2.1.0 / gems / RubyInline -3.12.4 / Library / inline.rb: 458: 3: error: expected expression Data_Get_Struct (self, MyStruct, pointer ); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/ruby.h:1038:20: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj); \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 460: 23: error: using an undeclared identifier Returning a pointer (rb_str_new2 (pointer- > title)); ^ * /. rbenv / versions / 2.1.2 / include / ruby-2.1.0 / ruby ​​/intern.h: 786: 27: note: extended from the macro 'rb_str_new_cstr' (__builtin_constant_p (str))? \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 460: 23: error: use of undeclared identifier 'pointer' * /. rbenv / versions / 2.1.2 / include / ruby-2.1.0 / ruby ​​/intern.h: 787: 14: note: extended from the macro 'rb_str_new_cstr' rb_str_new ((str), (long) strlen (str)) : \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 460: 23: error: use of undeclared identifier 'pointer' * /. rbenv / versions / 2.1.2 / include / ruby-2.1.0 / ruby ​​/intern.h: 787: 33: note: extended from the macro 'rb_str_new_cstr' rb_str_new ((str), (long) strlen (str)) : \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 460: 23: error: use of undeclared identifier 'pointer' * /. rbenv / versions / 2.1.2 / include / ruby-2.1.0 / ruby ​​/intern.h: 788: 18: note: extended from the macro 'rb_str_new_cstr' rb_str_new_cstr (str); \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 460: 10: error: returning 'void' from a function with an incompatible result type "VALUE" (aka "unsigned long") return (rb_str_new2 (pointer-> title)); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~. * / Rbenv / version / 2.1.2 / Lib / ruby ​​/ stones / 2.1.0 / gems / RubyInline -3.12.4 / Library / inline.rb: 478: 3: Error: using an undeclared identifier Pointer MyStruct MyStruct *; ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 478: 13: error: use of undeclared identifier pointer "pointer" MyStruct *; ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 480: 35: error: use of undeclared identifier 'pointer' Data_Get_Struct (self, MyStruct, pointer); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/ruby.h:1038:6: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj); \ ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 480: 25: error: using undeclared identifier 'MyStruct' Data_Get_Struct (self, MyStruct, pointer); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/ruby.h:1038:15: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj); \ ^. * / Rbenv / version / 2.1.2 / Lib / ruby ​​/ stones / 2.1.0 / gems / RubyInline -3.12.4 / Library / inline.rb: 480: 3: error: expected expression Data_Get_Struct (self, MyStruct, pointer ); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby /ruby.h: 1038: 20: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj) ; \ ^. * / Rbenv / version / 2.1.2 / Lib / ruby ​​/ stones / 2.1.0 / gems / RubyInline -3.12.4 / Library / inline.rb: 482: 3: Error: using undeclared identifier 'pointer' pointer-> title = StringValuePtr (value); ^. * / Rbenv / version / 2.1.2 / Lib / ruby ​​/ stones / 2.1.0 / gems / RubyInline -3.12.4 / Library / inline.rb: 456: 3: Error: using an undeclared identifier Pointer MyStruct MyStruct *; ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 456: 13: error: using an undeclared identifier pointer "pointer" MyStruct *; ^ * /. rbenv / versions / 2.1.2 / lib / ruby ​​/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb: 458: 35: error: use of undeclared identifier 'pointer' Data_Get_Struct (self, MyStruct, pointer); ^ /.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/ruby.h:1038:6: note: extended from the macro 'Data_Get_Struct' (sval) = (type) DATA_PTR (obj); \ ^ fatal error: too many errors have been emitted, now stops [-ferror-limit =]

I did a bunch of googling, but almost every example I found relates only to the simple use case that I set out at the beginning. The first error he sent was that MyStruct was not declared, so I tried to add a structure definition before the access methods, for example:

 builder.c ' typedef struct { char *title; int index; } MyStruct; ' 

This does nothing, and the documentation seems clear that the c method is only for declaring functions, not structures. I'm not quite sure what to try next, does anyone else have experience with this?

+5
source share
1 answer

RubyInline seems to suggest that you know how to write Ruby extensions if you are doing something more complex than the basics.

You need to define the structure yourself, as you tried, but you should use the prefix method, not the c method (which is for defining methods). You also need to define a distribution method for the class. You can do this with the c_singleton method by adding a function called allocate that RubyInline will recognize and use as a distribution function (it doesnt seem to be documented, I found it by looking at the source).

The combination looks something like this.

 class Foo inline do |builder| # First define the struct that will be wrapped in the # class. builder.prefix <<-DEFINE_STRUCT typedef struct { char* bar; } MyStruct; DEFINE_STRUCT # Next define the allocation function that will # allocate and wrap a MyStruct struct when creating a # new Foo object. You might want to initialize the values # to avoid possible segfaults. builder.c_singleton <<-ALLOCATE VALUE allocate() { MyStruct* pointer = ALLOC(MyStruct); return Data_Wrap_Struct(self, NULL, free, pointer); } ALLOCATE # Finally we can use the struct_name and accessor methods. builder.struct_name = 'MyStruct' builder.accessor 'bar', 'char *' end end 

Note that you can see the generated C code if you look under the .ruby_inline directory in your home directory.

+3
source

All Articles