I am working on a ruby extension for a C library, and having
difficulty of figuring out how to deal with this structure and
wrapping it nicely in Ruby. it is a command dispatch structure, where
you supply an array of function pointers:
/* command dispatch structure */
typedef int (*proxy_cmd)(int trans_id, int nargs, char **args);
struct proxy_commands {
int cmd_base;
int cmd_size;
proxy_cmd * cmd_funcs;
};
typedef struct proxy_commands proxy_commands;
/* end of definition */
Then, as a user you can define an array of function pointers like
this:
/* command dispatch structure */
typedef int (*proxy_cmd)(int trans_id, int nargs, char **args);
struct proxy_commands {
int cmd_base;
int cmd_size;
proxy_cmd * cmd_funcs;
};
typedef struct proxy_commands proxy_commands;
What you’re looking for is probably Data_Wrap_Struct and friends. See
ruby.h and intern.h. For a simple example of this in action, look at
the image_science.rb code (available via gems or on the seattlerb
project on rubyforge). It is only 228 lines long currently so it is a
pretty easy read.
Under normal usage, the library expects user to supply some functions
in C, such as func_a(), func_b(), through function pointers defined in
above dispatch command structure.
Now, from a wrapper stand point of view, I was hoping that the user-
provided function func_a(), func_b() etc. can be done by ruby as well
it will be very awkward for a user to use partial c, partial ruby
for the same library. I don’t see why rb_funcall() can help in this
case - in a sense, this case calls for a way to map ruby function into
C function, for the sake of using the C library call.
for the same library. I don’t see why rb_funcall() can help in this
case - in a sense, this case calls for a way to map ruby function into
C function, for the sake of using the C library call.
Write func_a, func_b, func_c as C functions that massage their arguments
into Rubyland and call rb_funcall to hit the user-written Ruby
functions.
Similarly massage the return values from Ruby back into C values.
Thanks for the suggestion, I will take a look at the sample lib you
suggested.
However, I don’t fully understand the reasoning of “not wrapping it”.
I was just suggesting that thinking laterally might be an
alternative. Obviously not if you’re using a specific 3rd party
library that you need to use. But if you just want an event engine,
we’re talking a TEENY amount of code in ruby (sans-leaks, sans-cores,
sans-icky) compared to C.
Here is my line of thinking:
the original C function register() expects an array of function
pointers, and those functions are suppose to be user-supplied and
implemented by C.
what users at what level/language?
Ideally, we should now allow those functions to be implemented by
ruby … is this possible at all?
I think you’d have to do some extra work to wrap up rb_funcall itself.
provided function func_a(), func_b() etc. can be done by ruby as well
Similarly massage the return values from Ruby back into C values.
Except that he wants the users to write func_[a-c] in ruby, to be
invoked by C, via c function pointers. It is that last teeny
requirement that I think blows this up.
case - in a sense, this case calls for a way to map ruby function into
C function, for the sake of using the C library call.
I suggested rb_funcall because it is the C entry point into a ruby
method. Since the function pointer signature doesn’t match up with
ruby’s, I don’t think you can share between C and ruby 1:1. But, you
might be able to wrap it up in such a way that you can call through
from C into ruby.
I wasn’t suggesting the user work in a mix of the two, just you.
Now, from a wrapper stand point of view, I was hoping that the user-
provided function func_a(), func_b() etc. can be done by ruby as well
it will be very awkward for a user to use partial c, partial ruby
for the same library. I don’t see why rb_funcall() can help in this
case - in a sense, this case calls for a way to map ruby function into
C function, for the sake of using the C library call.
Hi Oliver,
Does the library let you specify an argument to pass to these
callbacks when you register them somehow? If so, I’d probably define
the callbacks as Procs (or anything with #call) on the ruby side, and
in the C side, register the same function, say run_command(VALUE proc)
for all the commands, passing the Proc as an argument. run_command()
would just use rb_funcall to call the proc’s #call.
If not, and you do indeed have to register distinct C functions for
each command, two ideas come to mind:
Have a hard limit on the number of commands, and define C functions
func1, func2, … funcN to call the n-th one.
Use RubyInline to dynamically define each C function, load it as a
shared library, and add it to your struct (as Ryan just mentioned).
As for the interface on the ruby side, a few options come to mind.
The more direct translation of the C API:
EventMachine does exactly the same thing to invoke user-written event
handlers. The reactor core is in C. It calls a C stub that uses
rb_funcall
to hit Ruby code. EventMachine’s HTTP server also does the same thing.
The
HTTP server is in C but it calls out to user-written Ruby code to
fulfill
web requests.