C++ ORM framework for SQLite

Over the past week I’ve been rewrit­ing my rather dated SQLite wrap­per to have an ef­fi­cient, mod­ern C++ feel. The basic wrap­per is there, but I was look­ing for some­thing a lit­tle more this time.

While look­ing at the prob­lem I de­cided I was spend­ing too much time writ­ing boil­er­plate SQL for all my types so I de­cided to look at ex­ist­ing ORM frame­works. I’m pretty picky about my C++ though, and couldn’t find any­thing I liked so I started writ­ing my own. In­stead of cre­at­ing a tool to gen­er­ate C++, I wanted to take a pure ap­proach using na­tive C++ types and tem­plate metapro­gram­ming.

What I ended up with is not a full ORM frame­work, and I’m not par­tic­u­larly in­ter­ested in mak­ing it one. All I’m aim­ing for is re­mov­ing boil­er­plate code while leav­ing it easy to ex­tend it for more com­plex queries. Here’s what I’ve got so far:

struct my_object
{
  int id;
  std::string value;
  boost::posix_time::ptime time;
};

typedef boost::mpl::vector<
  sqlite3x::column<
    my_object, int, &my_object::id,
    sqlite3x::primary_key, sqlite3x::auto_increment
  >,
  sqlite3x::column<
    my_object, std::string, &my_object::value,
    sqlite3x::unique_key
  >,
  sqlite3x::column<
    my_object, boost::posix_time::ptime, &my_object::time
  >
> my_object_columns;

typedef sqlite3x::table<
  my_object,
  my_object_columns
> my_object_table;

Using it is pretty sim­ple. It uses the pri­mary key as ex­pected, gen­er­at­ing the proper WHERE con­di­tions and even ex­tract­ing the type to let find() and oth­ers spec­ify only the pri­mary key:

sqlite3x::connection con("test.db3");

my_object_table my_objects(con, "t_objects");

my_objects.add(my_object());
my_objects.edit(my_object());
my_objects.remove(int());
my_objects.exists(int());
my_objects.find(int());

One ben­e­fit of the ap­proach taken is it makes work­ing with sin­gle- and mul­ti­ple-in­her­i­tance just as easy:

struct my_derived :
  my_object
{
  float extra;
};

typedef boost::mpl::copy<
  boost::mpl::vector<
    sqlite3x::column<my_derived, float, &my_object::extra>
  >,
  boost::mpl::back_inserter<my_object_columns>
> my_derived_columns;

typedef sqlite3x::table<
  my_derived,
  my_derived_columns
> my_object_table;

The next thing on the list was sup­port­ing types not known na­tively to sqlite3x. I did not want to have the headache of sub-ta­bles, so I took the easy route and im­ple­mented basic se­ri­al­iza­tion sup­port:

struct my_derived :
  my_object
{
  std::vector<boost::uuid> uuids;
};

struct uuids_serializer
{
  static void serialize(std::vector<boost::uint8_t> &buffer,
     const std::vector<boost::uuid> &uuids);

  template<typename Iterator>
  static Iterator deserialize(std::vector<boost::uuid> &uuids,
     Iterator first, Iterator last);
};

typedef boost::mpl::copy<
  boost::mpl::vector<
    sqlite3x::column<
      my_derived, float, &my_object::extra,
      sqlite3x::serializer<uuids_serializer>
    >
  >,
  boost::mpl::back_inserter<my_object_columns>
> my_derived_columns;

A few things aren’t fin­ished, like spec­i­fy­ing in­dexes and sup­port for multi-col­umn pri­mary keys.

Over­all though, I’m pretty happy with it. The ma­jor­ity of what I use SQLite for doesn’t re­quire many com­plex queries, so this should greatly help lower the amount of code I have to man­age.

Best of all this ORM code is in an en­tirely iso­lated header file—if you don’t want it, just don’t in­clude it and you’ll still have ac­cess to all the basic SQLite func­tions. Even with it in­cluded I kept to the C++ mantra of “dont pay for what you don’t use”—as it is en­tirely tem­plate-dri­ven, code will only be gen­er­ated if you ac­tu­ally use it.

Once I’m fin­ished the code will re­place what I have up on the SQLite wrap­per page, but until then it will exist in the sub­ver­sion repos­i­tory only.

Posted on May 18, 2009 in C++, Coding, ORM, SQLite

Related Posts