- •Programming Ruby The Pragmatic Programmer's Guide
- •Foreword
- •Preface
- •Ruby Sparkles
- •What Kind of Language Is Ruby?
- •Is Ruby for Me?
- •Why Did We Write This Book?
- •Ruby Versions
- •Installing Ruby
- •Building Ruby
- •Running Ruby
- •Interactive Ruby
- •Ruby Programs
- •Resources
- •Acknowledgments
- •Notation Conventions
- •Roadmap
- •Ruby.New
- •Ruby Is an Object-Oriented Language
- •Some Basic Ruby
- •Arrays and Hashes
- •Control Structures
- •Regular Expressions
- •Blocks and Iterators
- •Reading and 'Riting
- •Onward and Upward
- •Classes, Objects, and Variables
- •Inheritance and Messages
- •Inheritance and Mixins
- •Objects and Attributes
- •Writable Attributes
- •Virtual Attributes
- •Class Variables and Class Methods
- •Class Variables
- •Class Methods
- •Singletons and Other Constructors
- •Access Control
- •Specifying Access Control
- •Variables
- •Containers, Blocks, and Iterators
- •Containers
- •Implementing a SongList Container
- •Blocks and Iterators
- •Implementing Iterators
- •Blocks for Transactions
- •Blocks Can Be Closures
- •Standard Types
- •Numbers
- •Strings
- •Working with Strings
- •Ranges as Sequences
- •Ranges as Conditions
- •Ranges as Intervals
- •Regular Expressions
- •Patterns
- •Anchors
- •Character Classes
- •Repetition
- •Alternation
- •Grouping
- •Pattern-Based Substitution
- •Backslash Sequences in the Substitution
- •Object-Oriented Regular Expressions
- •More About Methods
- •Defining a Method
- •Variable-Length Argument Lists
- •Methods and Blocks
- •Calling a Method
- •Expanding Arrays in Method Calls
- •Making Blocks More Dynamic
- •Collecting Hash Arguments
- •Expressions
- •Operator Expressions
- •Miscellaneous Expressions
- •Command Expansion
- •Backquotes Are Soft
- •Assignment
- •Parallel Assignment
- •Nested Assignments
- •Other Forms of Assignment
- •Conditional Execution
- •Boolean Expressions
- •Defined?, And, Or, and Not
- •If and Unless Expressions
- •If and Unless Modifiers
- •Case Expressions
- •Iterators
- •Break, Redo, and Next
- •Variable Scope and Loops
- •Exceptions, Catch, and Throw
- •The Exception Class
- •Handling Exceptions
- •Tidying Up
- •Play It Again
- •Raising Exceptions
- •Adding Information to Exceptions
- •Catch and Throw
- •Modules
- •Namespaces
- •Instance Variables in Mixins
- •Iterators and the Enumerable Module
- •Including Other Files
- •Basic Input and Output
- •What Is an io Object?
- •Opening and Closing Files
- •Reading and Writing Files
- •Iterators for Reading
- •Writing to Files
- •Talking to Networks
- •Threads and Processes
- •Multithreading
- •Creating Ruby Threads
- •Manipulating Threads
- •Thread Variables
- •Threads and Exceptions
- •Controlling the Thread Scheduler
- •Mutual Exclusion
- •The Mutex Class
- •Condition Variables
- •Running Multiple Processes
- •Spawning New Processes
- •Independent Children
- •Blocks and Subprocesses
- •When Trouble Strikes
- •Ruby Debugger
- •Interactive Ruby
- •Editor Support
- •But It Doesn't Work!
- •But It's Too Slow!
- •Create Locals Outside Blocks
- •Use the Profiler
- •Ruby and Its World
- •Command-Line Arguments
- •Command-Line Options
- •Program Termination
- •Environment Variables
- •Writing to Environment Variables
- •Where Ruby Finds Its Modules
- •Build Environment
- •Ruby and the Web
- •Writing cgi Scripts
- •Using cgi.Rb
- •Quoting
- •Creating Forms and html
- •Cookies
- •Sessions
- •Embedding Ruby in html
- •Using eruby
- •Installing eruby in Apache
- •Improving Performance
- •Ruby Tk
- •Simple Tk Application
- •Widgets
- •Setting Widget Options
- •Getting Widget Data
- •Setting/Getting Options Dynamically
- •Sample Application
- •Binding Events
- •Scrolling
- •Just One More Thing
- •Translating from Perl/Tk Documentation
- •Object Creation
- •Running Ruby Under Windows
- •Windows Automation
- •Getting and Setting Properties
- •Named Arguments
- •For each
- •An Example
- •Optimizing
- •Extending Ruby
- •Ruby Objects in c
- •Value as a Pointer
- •Value as an Immediate Object
- •Writing Ruby in c
- •Evaluating Ruby Expressions in c
- •Sharing Data Between Ruby and c
- •Directly Sharing Variables
- •Wrapping c Structures
- •An Example
- •Memory Allocation
- •Creating an Extension
- •Creating a Makefile with extconf.Rb
- •Static Linking
- •Embedding a Ruby Interpreter
- •Bridging Ruby to Other Languages
- •Ruby c Language api
- •The Ruby Language
- •Source Layout
- •Begin and end Blocks
- •General Delimited Input
- •The Basic Types
- •Integer and Floating Point Numbers
- •Strings
- •Requirements for a Hash Key
- •Symbols
- •Regular Expressions
- •Regular Expression Options
- •Regular Expression Patterns
- •Substitutions
- •Extensions
- •Variable/Method Ambiguity
- •Variables and Constants
- •Scope of Constants and Variables
- •Predefined Variables
- •Exception Information
- •Pattern Matching Variables
- •Input/Output Variables
- •Execution Environment Variables
- •Standard Objects
- •Global Constants
- •Expressions Single Terms
- •Operator Expressions
- •More on Assignment
- •Parallel Assignment
- •Block Expressions
- •Boolean Expressions
- •Truth Values
- •And, Or, Not, and Defined?
- •Comparison Operators
- •Ranges in Boolean Expressions
- •Regular Expressions in Boolean Expressions
- •While and Until Modifiers
- •Break, Redo, Next, and Retry
- •Method Definition
- •Method Arguments
- •Invoking a Method
- •Class Definition
- •Creating Objects from Classes
- •Class Attribute Declarations
- •Module Definitions
- •Mixins---Including Modules
- •Module Functions
- •Access Control
- •Blocks, Closures, and Proc Objects
- •Proc Objects
- •Exceptions
- •Raising Exceptions
- •Handling Exceptions
- •Retrying a Block
- •Catch and Throw
- •Classes and Objects
- •How Classes and Objects Interact
- •Your Basic, Everyday Object
- •Object-Specific Classes
- •Mixin Modules
- •Extending Objects
- •Class and Module Definitions
- •Class Names Are Constants
- •Inheritance and Visibility
- •Freezing Objects
- •Locking Ruby in the Safe
- •Safe Levels
- •Tainted Objects
- •Reflection, ObjectSpace, and Distributed Ruby
- •Looking at Objects
- •Looking Inside Objects
- •Looking at Classes
- •Looking Inside Classes
- •Calling Methods Dynamically
- •Performance Considerations
- •System Hooks
- •Runtime Callbacks
- •Tracing Your Program's Execution
- •How Did We Get Here?
- •Marshaling and Distributed Ruby
- •Custom Serialization Strategy
- •Distributed Ruby
- •Compile Time? Runtime? Anytime!
- •Standard Library
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; } |