There is one piece of software that I think every programmer should
know about. That is SQLite.
SQLite is a SQL database written in C (it has bindings for any programming
language of consequence) that can be compiled directly in to a C/C++
program. It is fully featured, it supports journaling and does atomic
operations using native file locking operations available from
the operating system. It runs without spawning an additional process to
arbitrate access to the database file.
SQLite is very fast for certain
types of workloads but doesn't scale to very high concurrency. However I've
used it with some concurrency and it seems to work well with the database
timeout set > 0. The SQLite website states that an example of a concurent
load beyond SQLite's capability would be a very high traffic website.
Anyways, I'm a C++ programmer and the SQLite API is in C which
requires quite a bit of boiler plate to get things working. Results to
queries are obtained by using a call back function. Needless to say you
can't register a C++ member function as a call back directly. SQLite
does give you a (void *) you can use to pass anything you want back to
the call back but obviously that isn't type safe and it requires boiler
plate within the call back to cast back to the desired type.
I wrote a wrapper which encapsulates all the boiler plate and presents
a more C++ like interface to sqlite. There are many existing C++ wrappers
out there but obviously I didn't like any of them since I wrote my own. :)
My wrapper lets you use a call back function or member function. It also
adds functions to pass a parameter to the call back in a type safe way.
Required Call Back:
//regular call back
int call_back(int columns, char ** response, char ** column_name);
//call back with object of any type (templated)
int call_back(std::string str, int columns, char ** response, char ** column_name);
Wrapper Examples:
//no call back
DB.query("DROP TABLE test");
//with function call back
DB.query("SELECT * FROM table", call_back);
DB.query("SELECT * FROM table", &Test, &test::call_back);
//with function call back with object
test Test;
std::string str = "test!";
DB.query("SELECT * FROM table", call_back_with_object, &str);
DB.query("SELECT * FROM table", &Test, &test::call_back_with_object, &str);
The easiest way to test this out is: download the wrapper + test program,
download the sqlite3 amalgamation, extract the amalgamation in to the wrapper
directory, and run the included Makefile.
Download: SQLite3 Wrapper + Test Program,
Wrapper Only, SQLite