Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Ruby Programming
Previous Page Home Next Page

Wrapping C Structures

Now on to the really fun 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 CDJukeBox structure, 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 (type T_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 is T_DATA and its Ruby class is class.
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-type is 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_Struct is 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 void pointer, a reference to your structure. The mark routine 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 using rb_gc_mark(value). If the structure doesn't reference other Ruby objects, you can simply pass 0 as a function pointer.

When the object needs to be disposed of, the garbage collector will call the free routine to free it. If you have allocated any memory yourself (for instance, by using Data_Make_Struct), you'll need to pass a free function---even if it's just the standard C library's free routine. 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);

info is a VALUE type, a genuine Ruby object of class Test (represented in C by the built-in type T_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 the VALUE:

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 initialize method, and a ``C-constructor.'' If you were writing Ruby source, you'd allocate and initialize an object by calling new. In C extensions, the corresponding call is Data_Make_Struct. However, although this allocates memory for the object, it does not automatically call an initialize method; 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 initialize in your class. Within initialize, it is allowable (but not necessarily advisable) to alter the existing data pointer, which may be accessed directly with DATA_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 Programming
Previous Page Home Next Page

 
 
  Published under the terms of the Open Publication License Design by Interspire