Examples Documentation Contact Downloads

Examples

The best way to get acquainted with Grace is to see it in action. On this page you will find a number of rudimentary examples of the Grace classes in action.

Example 1: Hello World

This is a short 'Hello World' program that splits a string of objects to greet into a value object, then uses foreach to loop through them and uses the %format system to print out individual greetings:

#include <grace/application.h>
#include <grace/filesystem.h>

class helloapp : public application
{
public:
    helloapp (void) : application ("hello")
    {
    }
    ~helloapp (void)
    {
    }
    
    int main (void)
    {
        value greetings =
            $("world", "Hello") ->
            $("everyone", "Hi") ->
            $("you", "Hey");
            
        foreach (g, greetings)
        {
            fout.writeln ("%s, %s." %format (g, g.id()));
        }
        
        return 0;
    }
};

$appobject (helloapp);

The value class gives you generic data objects that can store direct data or a tree using array or dictionary indexing. The output will look like this:

$ ./ex1
Hello, world.
Hi, everyone.
Hey, you.
$

Example 2: Command Line Arguments

Let's make things more interesting:

#include <grace/application.h>
#include <grace/filesystem.h>

class helloapp : public application
{
public:
    /// Constructor.
    /// Sets up the option parsing for this application. The --help
    /// option has a built-in binding.
    helloapp (void) : application ("hello")
    {
        opt = $("-l", $("long", "--lang")) ->
              $("-h", $("long", "--help")) ->
              $("--lang",
                    $("argc", 1) ->
                    $("help", "Language (en,nl)") ->
                    $("default", "en")
               );
    }
    
    /// Destructor.
    ~helloapp (void)
    {
    }
    
    /// Get a greeting apropriate for a given language.
    /// \param language the language code to look up.
    value *getgreetings (const statstring &language)
    {
        caseselector (language)
        {
            incaseof ("en") :
                return $("world", "Hello") ->
                       $("everyone", "Hi") ->
                       $("you", "Hey");
            
            incaseof ("nl") :
                return $("wereld", "Hallo") ->
                       $("iedereen", "Hoi") ->
                       $("jij", "Hee");
            
            defaultcase :
                return NULL;
        }
    }
    
    /// Main method.
    int main (void)
    {
        // --lang defaults to 'en' as per opt["--lang"]["default"];
        value greetings = getgreetings (argv["--lang"]);
        if (! greetings)
        {
            ferr.writeln ("Unrecognized language: %s" %format (argv["--lang"]));
            return 1;
        }
        
        foreach (g, greetings)
        {
            fout.writeln ("%s, %s." %format (g, g.id()));
        }
        
        return 0;
    }
};

$appobject (helloapp);
$version (1.0.1);

Now we added support for two languages and, by setting the application::opt array sensibly, exported an option flag to the command line. This is the result:

$ ./ex2 --help
-l --lang [en]: Language (en,nl)
$ ./ex2
Hello, world.
Hi, everyone.
Hey, you.
$ ./ex2 -l nl
Hallo, wereld.
Hoi, iedereen.
Hee, jij.
$ ./ex2 --lang fr
Unrecognized language: fr
$ 

Example 3: HTTP service

Let's make things a bit more exciting and use the httpd class to serve a dynamic webpage:

#include <grace/daemon.h>
#include <grace/httpd.h>

/// A serverpage sending out a nice greeting to the world
/// on the '/' URI.
class hellopage : public serverpage
{
public:
    /// Constructor: Set up serverpage and URI.
    /// \param pparent The httpd to attach to.
    hellopage (httpd &pparent) : serverpage (pparent, "/")
    {
    }
    
    /// Boring virtual destructor.
    ~hellopage (void)
    {
    }
    
    /// Serverpage execution, prints greeting.
    /// \param env Meta variables.
    /// \param argv POST or GET variables.
    /// \param out Output page.
    /// \param outhdr Output headers.
    int execute (value &env, value &argv, string &out, value &outhdr)
    {
        outhdr["Content-type"] = "text/plain";
        out = "Hello, world.\n";
        
        return 200;
    }
};

/// Main daemon object.
class helloserver : public daemon
{
public:
    /// Constructor. Set up command line arguments.
    helloserver (void) : daemon ("hello")
    {
        opt = $("-p", $("long", "--port")) ->
              $("-h", $("long", "--help")) ->
              $("--port",
                    $("argc", 1) ->
                    $("default", 1337) ->
                    $("help", "TCP listening port")
               );
    }
    
    /// Virtual destructor.
    ~helloserver (void)
    {
    }
    
    /// Main method. Starts the service and waits for an opportunity
    /// to shut down.
    int main (void)
    {
        /// Log in the current directory.
        addlogtarget (log::file, "event.log", log::all);
        
        /// Set up listening port from command line argument (or its
        /// provided default).
        int port = argv["--port"];
        srv.listento (port);
        log::write (log::info, "main", "Starting webserver "
                    "on *:%i" %format (port));

        /// Spawn to the background.
        daemonize ();
        
        /// Set up the httpd object chain and start the httpd threads.
        new hellopage (srv);
        srv.start ();
        
        while (true)
        {
            value ev = waitevent ();
            if (ev.type() == "shutdown") break;
        }
        
        /// Shut down auxiliary threads.
        log::write (log::info, "main", "Shutting down webserver");
        srv.shutdown ();
        
        log::write (log::info, "main", "Shutting down log thread");
        stoplog ();
        
        return 0;
    }
    
    httpd srv;
};

$appobject (helloserver);
$version (1.0);

The daemon class helps us with spawning to the background and writing to the logfile. This is the result:

$ ./ex3
main: Starting webserver on *:1337
$ telnet 127.0.0.1 1337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Content-type: text/plain
Content-length: 14

Hello, world.
Connection closed by foreign host.
$ killall -TERM ex3
$ cat event.log 
Dec  2 09:37:33 main     [INFO ]: Starting webserver on *:1337
Dec  2 09:37:35 main     [INFO ]: Shutting down webserver
Dec  2 09:37:40 main     [INFO ]: Shutting down log thread
$ 

Example 4: Memory database daemon

A more elaborate http-based example, this time we're making a simple http-based memory storage service:

#include <grace/daemon.h>
#include <grace/httpd.h>
#include <grace/lock.h>

//  -------------------------------------------------------------------------
/// HTTP handler object for a simple in-memor document store
//  -------------------------------------------------------------------------
class memstore : public httpdobject
{
public:
                     /// Constructor.
                     /// \param srv Reference to parent httpd.
                     memstore (httpd &srv);
                    ~memstore (void);
                    
                     /// Run-method.
                     /// \param uri The request URI
                     /// \param postbody Posted data
                     /// \param inhdr Input headers
                     /// \param out Output data
                     /// \param outhdr Output headers
                     /// \param env Meta-variables
                     /// \param s Raw socket.
    int              run (string &uri, string &postbody, value &inhdr,
                          string &out, value &outhdr, value &env,
                          tcpsocket &s);
                          
    value           *put (const statstring &uri, const value &v);
    value           *post (const statstring &uri, const value &v);
    value           *del (const statstring &uri);
                          
protected:
    lock<value>      db; ///< The memory database.
};

//  -------------------------------------------------------------------------
/// Main daemon class.
//  -------------------------------------------------------------------------
class memstored : public daemon
{
public:
                     memstored (void);
                    ~memstored (void);
                    
    int              main (void);
    httpd            srv;
};

// ==========================================================================
// CONSTRUCTOR memstore
// ==========================================================================
memstore::memstore (httpd &srv)
    : httpdobject (srv, "*")
{
}

// ==========================================================================
// DESTRUCTOR memstore
// ==========================================================================
memstore::~memstore (void)
{
}

// ==========================================================================
// METHOD memstore::put
// ==========================================================================
value *memstore::put (const statstring &uri, const value &dat)
{
    returnclass (value) res retain;
    
    exclusivesection (db)
    {
        if (! db.exists (uri))
        {
            db[uri] = dat;
            res = $("ok", true);
        }
        else
        {
            res = $("ok", false) -> $("error", "Resource exists");
        }
    }
    
    return &res;
}

// ==========================================================================
// METHOD memstore::post
// ==========================================================================
value *memstore::post (const statstring &uri, const value &dat)
{
    returnclass (value) res retain;
    
    exclusivesection (db)
    {
        if (db.exists (uri))
        {
            db[uri] = dat;
            res = $("ok", true);
        }
        else
        {
            res = $("ok", false) -> $("error", "Resource not found");
        }
    }
    
    return &res;
}

// ==========================================================================
// METHOD memstore::del
// ==========================================================================
value *memstore::del (const statstring &uri)
{
    returnclass (value) res retain;
    
    exclusivesection (db)
    {
        if (db.exists (uri))
        {
            db.rmval (uri);
            res = $("ok", true);
        }
        else
        {
            res = $("ok", false) -> $("error", "Resource not found");
        }
    }
    
    return &res;
}

// ==========================================================================
// METHOD memstore::run
// ==========================================================================
int memstore::run (string &uri, string &postbody, value &inhdr,
                   string &out, value &outhdr, value &env,
                   tcpsocket &s)
{
    value v;
    outhdr["Content-type"] = "application/json";
    
    caseselector (env["method"])
    {
        incaseof ("GET") :
            sharedsection (db)
            {
                if (db.exists (uri))
                {
                    const value &vv = db[uri];
                    outhdr["Content-type"] = vv["Content-type"];
                    out = vv["data"].sval();
                    breaksection return 200;
                }
            }
            
            v = $("ok",false) -> $("error","Not found");
            out = v.tojson ();
            return 404;
        
        incaseof ("POST") :
            v = $("Content-type",inhdr["Content-type"]) ->
                $("data", postbody);
            
            v = post (uri, v);
            out = v.tojson ();
            
            log::write (log::info, "memstore", "%P update <%s>"
                        %format (env["ip"], uri));
                        
            return v["ok"] ? 200 : 404;
            
        incaseof ("PUT") :
            v = $("Content-type",inhdr["Content-type"]) ->
                $("data", postbody);
            
            v = put (uri, v);
            out = v.tojson ();

            log::write (log::info, "memstore", "%P store <%s>"
                        %format (env["ip"], uri));
            
            return v["ok"] ? 200 : 405;
        
        incaseof ("DELETE") :
            v = del (uri);
            out = v.tojson ();

            log::write (log::info, "memstore", "%P delete <%s>"
                        %format (env["ip"], uri));
                        
            return v["ok"] ? 200 : 404;
        
        defaultcase :
            return 500;
        
    }
}

// ==========================================================================
// CONSTRUCTOR memstored
// ==========================================================================
memstored::memstored (void) : daemon ("memstored")
{
    opt = $("-p", $("long", "--port")) ->
          $("-h", $("long", "--help")) ->
          $("--port",
                $("argc", 1) ->
                $("default", 1135) ->
                $("help", "TCP listen port number"));
}

// ==========================================================================
// DESTRUCTOR memstored
// ==========================================================================
memstored::~memstored (void)
{
}

// ==========================================================================
// METHOD memstored::main
// ==========================================================================
int memstored::main (void)
{
    addlogtarget (log::file, "event.log", log::all);
    int port = argv["--port"];
    srv.listento (port);
    log::write (log::info, "main", "Starting server on "
                "port *:%i" %format (port));
    
    daemonize ();
    log::write (log::info, "main", "Starting threads");
    new memstore (srv);
    srv.start ();
    
    while (true)
    {
        value ev = waitevent ();
        if (ev.type() == "shutdown") break;
    }
    
    log::write (log::info, "main", "Stopping web service");
    srv.shutdown ();
    log::write (log::info, "main", "Shutting down log thread");
    stoplog ();
    
    return 0;
}

$appobject (memstored);
$version (1.0);

And the output of this one:

$ ./ex4
main: Starting server on port *:1135
$ telnet 127.0.0.1 1135
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
PUT /mynote HTTP/1.0
Content-type: application/json
Content-length: 44

{"doctype":"note","text":"This is a test"}
HTTP/1.1 200 OK
Content-type: application/json
Content-length: 12

{"ok":true}
Connection closed by foreign host.
$ tail event.log 
Dec  3 00:09:01 main     [INFO ]: Starting server on port *:1135
Dec  3 00:09:01 main     [INFO ]: Starting threads
Dec  3 00:10:05 memstore [INFO ]: 127.0.0.1 store </mynote>
$ curl http://127.0.0.1:1135/mynote
{"doctype":"note","text":"This is a test"}
$