Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ruby / Yukihiro Matsumoto_Programming Ruby.doc
Скачиваний:
121
Добавлен:
06.06.2015
Размер:
2.71 Mб
Скачать

Evaluating Ruby Expressions in c

If you are in the middle of some C code and you want to run an arbitrary Ruby expression without writing a bunch of C, you can always use the C version of eval. Suppose you have a collection of objects that need to have a flag cleared.

rb_eval_string("anObject.each{|x| x.clearFlag }");

If you just want to call a particular method (which is cheaper than eval-ing an entire string) you can use

rb_funcall(receiver, method_id, argc, ...)

Full descriptions of these and other commonly used C functions begin on page 186.

Sharing Data Between Ruby and c

We've covered enough of the basics now to return to our jukebox example---interfacing C code with Ruby and sharing data and behavior between the two worlds.

Directly Sharing Variables

Although you could maintain a C version of some variable along with a separate Ruby version of that variable, and struggle to keep the two in sync,[A clear violation of the DRY--Don't Repeat Yourself---principle described in our book The Pragmatic Programmer .]it would be much better to share a variable directly between Ruby and C. You can share global variables by creating a Ruby object on the C side and then binding its address to a Ruby global variable. In this case, the $ prefix is optional, but it helps clarify that this is a global variable.

VALUE hardware_list;

hardware_list = rb_ary_new();

rb_define_variable("$hardware", &hardware_list);

...

rb_ary_push(hardware_list, rb_str_new2("DVD"));

rb_ary_push(hardware_list, rb_str_new2("CDPlayer1"));

rb_ary_push(hardware_list, rb_str_new2("CDPlayer2"));

The Ruby side can then access the C variable hardware_listas$hardware:

$hardware

»

["DVD", "CDPlayer1", "CDPlayer2"]

You can also create hookedvariables that will call a specified function when the variable is accessed, andvirtualvariables that only call the hooks---no actual variable is involved. See the API section that begins on page 189 for details.

If you create a Ruby object from C and store it in a C global variable withoutexporting it to Ruby, you must at least tell the garbage collector about it, lest ye be reaped inadvertently:

VALUE obj;

obj = rb_ary_new();

rb_global_variable(obj);

Wrapping c Structures

Now on to the reallyfun stuff. We've got the vendor's library that controls the audio CD jukebox units, and we're ready to wire it into Ruby. The vendor's header file looks like this:

typedef struct _cdjb {

  int statusf;

  int request;

  void *data;

  char pending;

  int unit_id;

  void *stats;

} CDJukebox;

// Allocate a new CDPlayer structure and bring it online

CDJukebox *CDPlayerNew(int unit_id);

// Deallocate when done (and take offline)

void CDPlayerDispose(CDJukebox *rec);

// Seek to a disc, track and notify progress

void CDPlayerSeek(CDJukebox *rec,

                  int disc,

                  int track,

                  void (*done)(CDJukebox *rec, int percent));

// ... others...

// Report a statistic

double CDPlayerAvgSeekTime(CDJukebox *rec);

This vendor has its act together; while the vendor might not admit it, the code is written with an object-oriented flavor. We don't know what all those fields mean within the CDJukeBoxstructure, but that's okay---we can treat it as an opaque pile of bits. The vendor's code knows what to do with it, we just have to carry it around.

Anytime you have a C-only structure that you would like to handle as a Ruby object, you should wrap it in a special, internal Ruby class called DATA(typeT_DATA). There are two macros to do this wrapping, and one to retrieve your structure back out again.

C Datatype Wrapping

VALUE 

Data_Wrap_Struct(VALUE class, void (*mark)(), void (*free)(), void *ptr")

 

Wraps the given C datatype ptr, registers the two garbage collection routines (see below), and returns a VALUE pointer to a genuine Ruby object. The C type of the resulting object isT_DATAand its Ruby class isclass.

VALUE 

Data_Make_Struct(VALUE class, c-type, void (*mark)(), void (*free)(), c-type *")

 

Allocates a structure of the indicated type first, then proceeds as Data_Wrap_Struct.c-typeis the name of the C datatype that you're wrapping, not a variable of that type.

 

Data_Get_Struct(VALUE obj,c-type,c-type *")

 

Returns the original pointer. This macro is a type-safe wrapper around the macro DATA_PTR(obj), which evaluates the pointer.

The object created by Data_Wrap_Structis a normal Ruby object, except that it has an additional C datatype that can't be accessed from Ruby. As you can see in Figure 17.1 on page 177, this C datatype is separate from any instance variables that the object contains. But since it's a separate thing, how do you get rid of it when the garbage collector claims this object? What if you have to release some resource (close some file, clean up some lock or IPC mechanism, and so on)?

Figure not available...

In order to participate in Ruby's mark-and-sweep garbage collection process, you need to define a routine to free your structure, and possibly a routine to mark any references from your structure to other structures. Both routines take a voidpointer, a reference to your structure. Themarkroutine will be called by the garbage collector during its ``mark'' phase. If your structure references other Ruby objects, then your mark function needs to identify these objects usingrb_gc_mark(value). If the structure doesn't reference other Ruby objects, you can simply pass0as a function pointer.

When the object needs to be disposed of, the garbage collector will call the freeroutine to free it. If you have allocated any memory yourself (for instance, by usingData_Make_Struct), you'll need to pass a free function---even if it's just the standard C library'sfreeroutine. For complex structures that you have allocated, your free function may need to traverse the structure to free all the allocated memory.

First a simple example, without any special handling. Given the structure definition

typedef struct mp3info {

  char *title;

  char *artist;

  int  genre;

} MP3Info;

we can create a structure, populate it, and wrap it as an object.[We cheat a bit in this example. Our MP3Info structure has a couple of char pointers in it. In our code we initialize them from two static strings. This means that we don't have to free these strings when the MP3Info structure is freed. If we'd allocated these strings dynamically, we'd have to write a free method to dispose of them.]

MP3Info *p;

VALUE info;

p = ALLOC(MP3Info);

p->artist = "Maynard Ferguson";

p->title = "Chameleon";

...

info = Data_Wrap_Struct(cTest, 0, free, p);

infois aVALUEtype, a genuine Ruby object of classTest(represented in C by the built-in typeT_DATA). You can push it onto an array, hold a reference to it in an object, and so on. At some later point in the code, we may want to access this structure again, given theVALUE:

VALUE doit(VALUE info) {

MP3Info *p;

Data_Get_Struct(info, MP3Info, p);

...

p->artist -> "Maynard Ferguson"

p->title -> "Chameleon"

...

}

In order to follow convention, however, you may need a few more things: support for an initializemethod, and a ``C-constructor.'' If you were writing Ruby source, you'd allocate and initialize an object by callingnew. In C extensions, the corresponding call isData_Make_Struct. However, although this allocates memory for the object, it doesnotautomatically call aninitializemethod; you need to do that yourself:

info = Data_Make_Struct(cTest, MP3Info, 0, free, one);

rb_obj_call_init(info, argc, argv);

This has the benefit of allowing subclasses in Ruby to override or augment the basic initializein your class. Withininitialize, it is allowable (but not necessarily advisable) to alter the existing data pointer, which may be accessed directly withDATA_PTR(obj).

And finally, you may want to define a ``C-constructor''---that is, a globally available C function that will create the object in one convenient call. You can use this function within your own code or allow other extension libraries to use it. All of the built-in classes support this idea with functions such as rb_str_new,rb_ary_new, and so on. We can make our own:

VALUE mp3_info_new() {

  VALUE info;

  MP3Info *one;

  info = Data_Make_Struct(cTest, MP3Info, 0, free, one);

  ...

  rb_obj_call_init(info, 0, 0);

  return info;

}

Соседние файлы в папке Ruby