RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


The Road to Ruby from C++ : Page 3

C++ developers learn Ruby at their own peril. Once they become familiar with this powerfully concise language, they may find returning to C++ a bitter pill to swallow.

Visibility and Access Control
The final class in the example applications is implemented starting at line 67 in Listing 1 and line 54 in Listing 2. This class iterates through all of the files in the provided directory, breaking the file into tokens and counting the occurrences of each word. It also defines a method to dump the XML output.

Both C++ and Ruby support public, protected, and private members. In the example, the method count_words_in_file is declared as private in both implementations.

In both C++ and Ruby, public methods can be called by anyone, and protected methods can be called only by objects of the same class or objects that inherit from the defining class. The semantics of private differ between C++ and Ruby, however. In C++, methods are private to the class, while in Ruby they are private to the instance. In other words, you can never explicitly specify the receiver for a private method call in Ruby.

Blocks and Closures
One feature of Ruby for which C++ has no good counterpart is its support of blocks. The most common use for blocks is iteration. You'll find examples of iteration with blocks in Listing 2's implementation of the WordCounter class at lines 65, 73, and 76.

Consider the code at line 65 for example:

    Dir.foreach(".") { |filename|
       count_words_in_file filename 

The class Dir is used to inspect directories. The method foreach is passed two arguments: the string "." and the block of code in parentheses, which in turn specifies an argument filename within the vertical bars. The method foreach iteratively invokes the block, passing in the names of each file found in the current working directory ".". This simple feature can save significant keystrokes and also leads to very readable code.

Blocks also are useful for more than just iteration. Line 48 uses a block to specify the comparison function to use for sorting an array of pairs:

  def file_occurrences
    return @file_hash.sort { |x,y| –(x[1]<=>y[1]) }

The expression x <=> y is -1 if x < y, 0 if x == y, and 1 if x > y. The above code returns an array of pairs, where each pair consists of a String and a FixNum. The block specifies that the second element of each pair (the FixNum) should be compared using the negation of the <=> operator. This method therefore returns the word occurrences pairs in decreasing order of occurrences.

At line 15 of Listing 1, the C++ example code also specifies a comparison function to be used for ordering pairs. However, lacking support for anonymous functions, the comparison is implemented as a function object, later used to define the STL multiset at line 25.

The blocks in Ruby are so useful in part because they are closures. A closure captures the context in which it is defined, so it can refer to local variables found within the scope of the definition. Take for example the code at line 76 of Listing 2:

      wc.file_occurrences.each { |pair| 
        f = e.add_element "file", {"occurrences"=>"#{pair[1]}"}
        f.add_text pair[0]

The expression wc.file_occurrences returns an array of pairs. The array's method each is then invoked with the subsequent block as an argument. It's important to note that the block will be invoked from within the method each. However, because the block is a closure, it can still access the object e (which represents an XML element) that was in the local scope of the method where the block was defined.

While you can use C++ function objects to implement much of the functionality described above, I believe that the elegance and readability of blocks speak for themselves.

A Wide Range of Libraries, Regular Expressions
Another clear advantage that Ruby has over C++ is the vast collection of libraries that come with the standard distribution, as well as its support for regular expressions. For example, compare the ad-hoc implementation of an XML generator in method dump_results in Listing 1 to the use of the REXML library in Listing 2. Next, note the use of the pseudo-standard dirent.h for working with directories in C++ to that of the class Dir in the Ruby implementation. Finally, compare the ad-hoc parsing of the files at line 77 of Listing 1 to the much more concise code at line 90 of Listing 2.

While many available C++ libraries, such as Boost, provide a wide range of utilities, Ruby provides many of these features as part of the standard distribution. So out-of-the-box, the Ruby programming language and its standard libraries simplify many of the more common programming tasks when compared to C++.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date