Creating C structs using the RubyInline gem - c

I've got a Rails app that needs to show the results of a Monte Carlo sim, and for the first time, Ruby just isn't fast enough for my needs. So, I started poking around to see if I could rewrite my simulation in C and use those results in Ruby, and some googling turned up the RubyInline gem for easily writing faster C code directly in Ruby. Doing simple stuff works great, for instance, 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, it should definitely speed up my sim since it's pretty much just generating random numbers and adding stuff up based on those results. Unfortunately, beyond basic stuff like this, I can't figure out how to actually write my program. I need to pass some structs around to act as state variables, so the first order of business is figuring out how to create a C struct. Reading the documentation, it seems like it should be fairly simple.
accessor(method, type, member = method)
Adds a reader and writer for a C struct member wrapped via
Data_Wrap_Struct. method is the ruby name to give the accessor, type
is the C type. Unless the C member name is overridden with member, the
method name is used as the struct member.
builder.struct_name = 'MyStruct'
builder.accessor :title, 'char *'
builder.accessor :stream_index, 'int', :index
The documentation isn't entirely clear to me, but I assume that I'd put that in a class much like before 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'
Unfortunately, I can't even get that far
*/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb:456:3:
error: use of undeclared identifier
'MyStruct' MyStruct *pointer; ^
*/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb:456:13: error: use of undeclared identifier
'pointer' MyStruct *pointer;
^
*/.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: expanded from 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: use of 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: expanded from 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: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: expanded from 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: use of undeclared identifier
'pointer' return (rb_str_new2(pointer->title));
^
*/.rbenv/versions/2.1.2/include/ruby-2.1.0/ruby/intern.h:786:27: note: expanded from 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: expanded from 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: expanded from 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: expanded from 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 incompatible result type 'VALUE' (aka 'unsigned long') return (rb_str_new2(pointer->title));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb:478:3:
error: use of undeclared identifier
'MyStruct' MyStruct *pointer; ^
*/.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' MyStruct *pointer;
^
*/.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: expanded from 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: use of 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: expanded from 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: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: expanded from 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:482:3:
error: use of undeclared identifier
'pointer' pointer->title = StringValuePtr(value); ^
*/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb:456:3:
error: use of undeclared identifier
'MyStruct' MyStruct *pointer; ^
*/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/RubyInline-3.12.4/lib/inline.rb:456:13: error: use of undeclared identifier
'pointer' MyStruct *pointer;
^
*/.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: expanded from macro 'Data_Get_Struct'
(sval) = (type)DATA_PTR(obj);\
^ fatal error: too many errors emitted, stopping now [-ferror-limit=]
I've done a bunch of googling, but pretty much every example I've found deals only with the simple use case I outlined at first. The first error it spit out was that MyStruct was undeclared, so I tried adding a struct definition before the accessor methods, like so:
builder.c '
typedef struct
{
char *title;
int index;
} MyStruct;
'
That doesn't do anything, and the documentation seems to be clear that the c method is only for declaring functions, not structs. I'm not really sure what to try next, does anyone else have some experience with this?

RubyInline seems to assume that you know your way around writing Ruby extensions if you are doing anything more complex than the basics.
You do need to define the struct yourself as you tried, but you should use the prefix method rather than the c method (which is for defining methods). You also need to define the allocation method for the class. You can do this with the c_singleton method by adding a function named allocate which RubyInline will recognise and use as the allocation function (this doesn’t seem to be documented, I found it by looking at the source).
Putting it all together 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 you can see the generated C code if you look under the directory .ruby_inline in your home directory.

Related

Why is this generic expression bugging out? [duplicate]

This question already has answers here:
Incompatible pointer types passing in _Generic macro
(2 answers)
Closed 6 years ago.
Recently I answered some question asking how to implement complex const correctness of a structure member. Doing this I used Generic expressions and encountered strange behavior. Here is the sample code:
struct A
{
union {
char *m_ptrChar;
const char *m_cptrChar;
} ;
};
#define ptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar, \
const struct A *: a->m_cptrChar, \
struct A: a.m_ptrChar, \
const struct A: a.m_cptrChar)
void f(const struct A *ptrA)
{
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
}
On GCC and Clang the generic expression select the case when the type of a must be struct A which doesn't make any sense. When I comment the last two cases though it's working fine. Why is this - is it some bug?
The exact error messages on clang are:
test.c:17:5: error: member reference type 'const struct A *' is a pointer; did you mean to use '->'?
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
^
test.c:12:45: note: expanded from macro 'ptrChar_m'
struct A: a.m_ptrCHar, \
^
test.c:17:5: error: member reference type 'const struct A *' is a pointer; did you mean to use '->'?
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
^
test.c:13:51: note: expanded from macro 'ptrChar_m'
const struct A: a.m_cptrChar)
^
test.c:17:21: error: cannot assign to variable 'ptrA' with const-qualified type 'const struct A *'
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
^
test.c:15:24: note: variable 'ptrA' declared const here
void f(const struct A *ptrA)
^
test.c:23:18: error: invalid application of 'sizeof' to a function type [-Werror,-Wpointer-arith]
return sizeof(main);
4 errors generated.
Although the non-matching selections are not evaluated, the compiler is still compiling the code. So using the . operator on a pointer to a structure is not allowed.
If your input expression is always a variable, you could take the address of the variable representing the structure instance.
#define AptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar, \
const struct A *: a->m_cptrChar)
#define ptrChar_m(a) AptrChar_m(_Generic(a, struct A *: a, \
const struct A *: a, \
struct A: &a, \
const struct A: &a))

Accessing struct variables in a function pthread

What is the proper way to access the struct variables in a function that is called by pthread_creation. This is how I am trying to do
void *add_first_quat(void *a){
struct thread1Struct *myArray = (struct thread1Struct *)a;
int i ;
for(i= *myArray>start; i < *myArray>end; i++){
sum+= *myArray>th1Array[i];
}
/* the function must return something - NULL will do */
return NULL;
}
And in my struct I am defining two variables and pointer to a globally defined array
struct thread1Struct{
int start = 0;
int end = 25;
int *th1Array = myArray;
};
This is how I am calling pthread_create function
(pthread_create(&inc_first_quater_thread, NULL, add_first_quat, (void*) &th1StrObj))
Why my code is not working? I am getting following errors
main.c: In function ‘add_first_quat’:
main.c:14:9: error: dereferencing pointer to incomplete type
for(i= *myArray>start; i < *myArray>end; i++){
^
main.c:14:18: error: ‘start’ undeclared (first use in this function)
for(i= *myArray>start; i < *myArray>end; i++){
^
main.c:14:18: note: each undeclared identifier is reported only once for each function it appears in
main.c:14:29: error: dereferencing pointer to incomplete type
for(i= *myArray>start; i < *myArray>end; i++){
^
main.c:14:38: error: ‘end’ undeclared (first use in this function)
for(i= *myArray>start; i < *myArray>end; i++){
^
main.c:15:9: error: dereferencing pointer to incomplete type
sum+= *myArray>th1Array[i];
^
main.c:15:18: error: ‘th1Array’ undeclared (first use in this function)
sum+= *myArray>th1Array[i];
^
main.c: At top level:
main.c:34:12: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘=’ token
int start = 0;
^
First problem (syntax):
Try myArray->start or (*myArray).start or myArray[0].start. The first syntax would be my preference in this case.
Second problem:
dereferencing pointer to incomplete type
You need to provide the full declaration before referencing any fields.
Solve the problem by moving the full declaration of the struct to the top of your code file, or put it in a .h file that you #include in all source files that use the struct.
This: *myArray>start is not the proper syntax for accessing members of a pointer to a struct.
You could do this: (*myArray).start, which dereferences the pointer so *myArray is of type struct thread1Struct, then use . for member access.
The preferred way is myArray->start, where the -> operator does member access for a pointer to struct.
The problem lies in the way you're accessing the elements of the structure. Your expression *myArray>start makes no sense to the compiler. As you know, myArray is a pointer to a struct. You can access the data members in two ways :
You could use the indirection operator (eg. (*myArray).start )
You could use the arrow operator (eg. myArray->start)
This is how you access data members of any struct pointer. It does not pertain p-threads alone.

Multitude of struct errors

It's an extremely simple and useless piece of practice code I'm working with in what is starting to seem like and extremely useless book. I was doing a struct exercise, and upon code compilation I received a handful of errors.
Here's the offending code:
struct fish = {
const char *name;
const char *species;
int teeth;
int age;
};
void catalog(struct fish f)
{
printf("%s is a %s with %i teeth. He is %i.\n", f.name, f.species, f.teeth, f.age);
}
int main()
{
struct fish snappy = {"Snappy", "piranha", 69, 4};
catalog(snappy);
return 0;
}
This is the exact code from the book, minus the struct definition above catalog. I ended up just copy pasting because I started to suspect this book was just dead wrong. The book claimed that the above code should compile and run without the struct even being defined. I've tried putting the struct definition into a header file, and I've tried removing it or adding it to different parts of the code. I get the same exact errors:
snappy.c:8:13: error: expected identifier or ‘(’ before ‘=’ token
struct fish = {
^
snappy.c:16:26: error: parameter 1 (‘f’) has incomplete type
void catalog(struct fish f)
^
snappy.c: In function ‘main’:
snappy.c:24:12: error: variable ‘snappy’ has initializer but incomplete type
struct fish snappy = {"Snappy", "piranha", 69, 4};
^
snappy.c:24:12: warning: excess elements in struct initializer
snappy.c:24:12: warning: (near initialization for ‘snappy’)
snappy.c:24:12: warning: excess elements in struct initializer
snappy.c:24:12: warning: (near initialization for ‘snappy’)
snappy.c:24:12: warning: excess elements in struct initializer
snappy.c:24:12: warning: (near initialization for ‘snappy’)
snappy.c:24:12: warning: excess elements in struct initializer
snappy.c:24:12: warning: (near initialization for ‘snappy’)
snappy.c:24:17: error: storage size of ‘snappy’ isn’t known
struct fish snappy = {"Snappy", "piranha", 69, 4};
struct fish = { is wrong in struct declaration. It should be struct fish {. Remove = sign.

C struct declaration and initialization

I have been following tutorials from the web on creating a struct and then initializing it in main(). From the tutorials I have followed, I have created my own example, which is as follows:
#include <stdio.h>
struct test {
int num;
};
main() {
test structure;
}
However, this does not work:
test.c: In function 'main':
test.c:8: error: 'test' undeclared (first use in this function)
test.c:8: error: (Each undeclared identifier is reported only once
test.c:8: error: for each function it appears in.)
test.c:8: error: expected ';' before 'structure'
But when I change:
test structure;
to:
struct test structure;
the code compiles. Why is this though? From the numerous examples I have looked at it seems that I shouldn't need the 'struct' before 'test structure'.
Thanks for your help/comments/answers.
You were reading C++ examples. In C the type of your structure is struct test not test.
You can get around that by doing
typedef struct test_s
{
int num;
} test;

How can we remove undeclared function error when function is declared with typedef in c?

When i have run the following piece of code:
typedef char *lrfield();
struct lrfields {
char name[26];
lrfield *f;
};
struct lrfields lr_table[] = {
{"pri_tran_code1", pri_tran_code2},
{"sec_tran_code", sec_tran_code},
{"type_code", type_code},
{"sys_seq_nbr", sys_seq_nbr},
{"authorizer", authorizer},
{"void_code", void_code},
{"",0}
};
char *pri_tran_code2()
{
return pri_tran_code;
}
*
*
if(second)
{
for(bp=lr_table; bp->name[0]; bp++)
if(strcmp(bp->name, second)==0)
{
tmpval=bp->f();
break;
}
}
I have got these errors:
error: `pri_tran_code2' undeclared here (not in a function)
error: initializer element is not constant
error: (near initialization for `lr_table[0].f')
error: initializer element is not constant
error: (near initialization for `lr_table[0]')
error: initializer element is not constant
error: (near initialization for `lr_table[1]')
As you can see in the code that i have defined 'pri_tran_code2' above its call. Please help me to solve this error.
Add char *pri_tran_code2(); before you mention this name? Or simply move the whole implementation there. It doesn't matter where you call it, what matters is where you refer to it.
Your declaration is erroneous. To declare a function (function-pointer) type, try this instead:
typedef char *(*lrfield)();

Resources