Examples Documentation Contact Downloads

Data Classes in Action

This chapter contains a couple of examples of the core Grace classes as typically used inside a real application.

The Code

Let's take a look at how you typically use a couple of the data classes. A bit of fictional code:

#include <grace/str.h> 
#include <grace/value.h>

// Main application method 
int myApp::main (void) 
{ 
    string mystr = "Hello, world"; 
    statstring statstr = "messageOfPeace";
    value val = $(statstr, mystr) ->
                $("ultimateAnswer", 42);

    if (mystr != val[statstr]) 
    { 
        ferr.writeln ("Something's wrong with this world!"); 
        return 1; 
    }
    
    // Clear the string and put some formatted text in 
    mystr = "Message of Peace: %[messageOfPeace]s\n"
            "Ultimate Answer: %[ultimateAnswer]i\n" %format (val);

    // Send the string to the output channel. 
    fout.puts (mystr); 
    return 0; 
}

In this example we use a value object val as an associative array containing two key/value pairs. One is a string, the other is an integer. We use a statstring object as a key inside the value, but regular string constants work just as well. Then we put some formatted text inside a string object and send it to the output channel.

Inline value Declaration

The $(...) statement used above is an example of the inline declaration syntax. Both the root namespace and the value class carry a number of functions starting with '$'. In the root namespace, such a declaration creates a temporary value object. As a member method, it instructs the existing temporary object to append a new node. Here's how that works out:

value v;

v = $("one"); // ["one"]
v = $("one")->$("two"); // ["one","two"]
v = $("hello","world"); // {"hello":"world"}

// Dictionary with two entries, where the second entry is itself
// an array with two nodes:
// {"user":"john","favoriteColors":["green","yellow"]}
v = $("user","john") ->
    $("favoriteColors", $("green")->$("yellow"));

// Dictionary with type() = "person", two entries an an attribute:
v = $type("person") ->
    $attr("personclass","nerd") ->
    $("firstname","John") ->
    $("lastname","Doe");

// A value object set to an integer with an attribute:
v = $attr("src","deepthought") -> $val(42);

String Formatting

The %format construct is a clever way to get object data converted to a formatted string without explicitly casting everything down to C primitives. In its most basic use, it resembles the libc (s)printf syntax:

   string s;
   
   s = "%s" %format ("hi"); // string
   s = "%s %s" %format ("hello","world"); // two strings
   s = "%i" %format (144); // an int
   s = "%U" %format (314159265ULL); // an unsigned 64 bits int.
   s = "%8s" %format ("waytoolong"); // left-align and crop
   s = "%-4s" %format ("hi"); // --> "  hi"
   s = "%.2f" %format (1.5); // 1.50

With everything cast through a value object, format string errors are suddently a lot harder:

   s = "%s" %format (31337); // this is actually *FINE*
   s = "%s %s" %format ("hello"); // so is this

You're also allowed to mess around a bit more with the order of your data or recall dictionary nodes of the first argument by name, so you can do stuff like this:

s = "%{1}s, %{0}s." %format ("John","Conner");

value v = $("first","John") -> $("last","Doe");
s = "%[last]s, %[first]s" %format (v);

Finally, there's a bunch of extra tokens for encoding or escaping certain data types:

Token Description
%S Backslash-style escape of quotes, percentage-style escape of non-ASCII characters.
%X 64 bits hexadecimal
%M Escape for MySQL insertion with apropriate quotes — your query template need not supply the surrounding quotes.
%Q ANSI-SQL variant of %M
%P Represent as an IPv4 address
%Z Escape xml/html-sensitive characters as entities.
%! Encode the value object as compact xml
%J Encode the value object as JSON
%~ Escape as string with urlencoding

Data Objects and Memory Management

Let's take a look at how Grace data objects are passed to and from functions and methods in a way that frees your mind from worrying about memory management.

#include <grace/str.h> 
#include "myapplication.h" 

class personalName 
{ 
public: 
    personalName (void) 
    { 
        _first = "John"; 
        _last = "Doe"; 
    } 
    
    ~personalName (void) { } 
    
    // Accessor/Setter functions 
    const string &first (void) const { return _first; } 
    const string &last (void) const { return _last; } 
    void first (const string &arg) { _first = arg; } 
    void last (const string &arg) { _last = arg; } 
    
    // Combine the two strings into a new string. 
    string *makeFullName (void) 
    { 
        returnclass (string) result retain; 
        result = _first; 
        result += " "; 
        result += _last; 
        return &result; 
    } 
    
protected: 
    string _first; 
    string _last; 
};

// Main application method. 
int myApp::main (void) 
{ 
    personalName nom; 
    string fname; 
    
    nom.first ("Miles"); 
    nom.last ("Davis"); 
    
    fname = nom.makeFullName (); 
    fout.writeln (fname); 
    return 0; 
}

We defined a class here containing two string objects. Accessor methods for reading or writing these protected strings work on the basis of const references. The makeFullName method combines the two strings into a new string and returns this to the caller. This is where Grace does some magic for you. All data classes used in grace display special behavior when they are assigned with a pointer to an object of their own class (either through a copy-constructor or the '=' operator): Instead of bluntly copying the other object's data, it steals its data references and silently removes the temporary object from memory. This mechanism is also set to work between value, class name="stringclass name=" and statstring classes, so stuff like this is fine:

string *getAnswer (void)
{
    returnclass (string) res retain;
    res = "fourty-two";
    return &res;
}

int myApp::main (void)
{
    value knowledge;
    knowledge["universalAnswer"] = getAnswer();
}

The returnclass...retain macro creates a temporary object and a temporary reference to it. The object is sent to the caller by deriving the pointer from this reference again. We go through this trouble to give us some convenience in dealing with all the object's overloaded operators, which is somewhat tricky if you only have a pointer. The macro also ensures that the temporary object's memory is allocated from a static pool outside the normal memory allocator. This makes the process faster and allows the Grace library to detect leaks if a caller doesn't assign the return-value coming out of a method to an object to catch its allocation.

Working with IPv4 Addresses

The ipaddress class is pretty easy to use. Here's an example of the class in action:

#include "myapp.h"

int myapp::main (void)
{
    ipaddress cnnip;
    value resolved = netdb::gethostbyname ("cnn.com");
    fout.writeln ("cnn.com: %P" %format (cnnip));
    fout.puts (resolved.toxml());
    return 0;
}

Output of the above:

$ ./ipv4test
cnn.com: 64.236.16.20
<?xml version="1.0" encoding="UTF-8"?>
<dict id="resolved">
	<array id="name">
		<string>cnn.com</string>
	</array>
	<array id="address">
		<ipaddress>64.236.16.20</ipaddress>
		<ipaddress>64.236.16.52</ipaddress>
		<ipaddress>64.236.24.12</ipaddress>
		<ipaddress>64.236.29.120</ipaddress>
	</array>
</dict>
$ 

Exploring Data Classes with the Grace Shell

If you want to quickly explore the data classes interactively, make sure you have the grace-util package installed together with Grace. With this in place, the grace command with no arguments will spawn an interactive shell. Grace is not an interpreted language, so this shell is of limited use, but it can be useful for exploring the basic classes. Here's what a session in the Grace shell looks like;

$ grace
>>> print netdb::gethostbyname ("cnn.com")
{"name":["cnn.com"],"address":["64.236.16.20","64.236.16.52","64.236.24.12"]}
>>> string etcprotocols = fs.load ("etc:protocols")
>>> print etcprotocols.strlen()  
5766
>>> value ln = strutil::splitlines (etcprotocols)
>>> print ln[8] 
ip	0	IP		# internet protocol, pseudo protocol number
>>> value protocols                                       
>>> foreach (line, ln) {
  >   if (line.sval() && line.sval()[0] == '#') continue;
  >   value splt = strutil::splitspace (line)             
  >   if (splt.count()>2) {                   
  >     protocols[splt[0]] = $("val",splt[1].ival()) -> $("desc",splt[2])
  >   }                                                                   
  > }   
>>> print protocols["sctp"] 
{"val":132,"desc":"SCTP"}
>>> print protocols["icmp"] 
{"val":1,"desc":"ICMP"}
>>> 

Due to the compiled nature of C++, the shell is not all-powerful. Variables that persist can only be of a type that can be stored inside a value object, with the exception of those declared within a {...} block.

 

Previous Chapter Table of Contents Next Chapter