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 2: Command Line Arguments
Example 3: HTTP service
Example 4: Memory database daemon
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"} $