Archive for the ‘C++’ Category

ODB 1.1.0 released

Wednesday, January 26th, 2011

ODB 1.1.0 was released today. In case you are not familiar with ODB, it is an object-relational mapping (ORM) system for C++. It allows you to persist C++ objects to a relational database without having to deal with tables, columns, or SQL, or manually writing any of the mapping code.

This version took a bit longer than we initially planned, mainly because many of the new features depended on each other and, once we implemented one, then it also made sense to implement the others. For example, bidirectional object relationships (see below) are pretty much useless in any real-world scenario without support for lazy loading.

But, in the end, the result is a version that is packed with major new features, which I am going to examine next.

Containers

It is now possible to store containers in the database. Built-in support is provided for most standard C++ containers, namely, vector, list, map, multimap, set, and multiset. For example, now you can write:

#pragma db object
class person
{
  ...
 
  std::set<std::string> emails_;
};

And that’s it. That’s all you need to do. It is also very easy to add support for custom container types. All you need to do is provide a traits specialization for your container (see the ODB manual for details). Plus we are working on profile libraries that will provide support for containers found in popular framework and libraries, such as Boost and Qt.

Relationships

ODB now supports unidirectional (to-one, to-many) and bidirectional (one-to-one, one-to-many, many-to-many) object relationships. For example:

#pragma db object
class employer
{
  ...
 
  #pragma db inverse(employer_)
  std::vector<lazy_weak_ptr<employee> > employees_;
};
 
#pragma db object
class employee
{
  ...
 
  shared_ptr<employer> employer_;
};

In the above code fragment we used lazy_weak_ptr, which is a lazy pointer. The object that it points to is only loaded when requested.

In bidirectional relationships it is also possible to declare one side inverse, as shown in the code above. This results in the canonical relational model where only one table contains a reference to the other table.

Similar to containers, ODB provides built-in support (and the corresponding lazy pointers) for standard C++ pointers, namely, the raw pointer, auto_ptr, and TR1 shared_ptr/lazy_ptr. At the same time, profile libraries will provide support for smart pointers found in popular framework and libraries, such as Boost shared_ptr and Qt QSharedPointer. It is also easy to add support for your own smart pointers, as described in the ODB manual.

Composite Values

ODB prior to 1.1.0 only supported simple (single-column) value types. Now you can declare and use composite (multi-column) value types, for example:

#pragma db value
class name
{
  ...
 
  std::string first_;
  std::string last_;
};
 
#pragma db object
class person
{
  ...
 
  name name_;
};

Of course, all these features work well with each other. For example, you can have a container of object pointer or composite value types. Similarly, a composite value type can have members that are other composite value types, object pointers, or containers. Plus, these features are integrated into the query language so that you can use data members from composite values or related objects in query expressions, for example:

typedef odb::query<person> person_query;
typedef odb::query<employee> employee_query;
 
db.query<person> (person_query::name::last == "Doe");
db.query<employee> (employee_query::employer::name == "Example Inc");

There are also other important features in this release, including, optional object cache (session), customizable object pointers, native SQL statement execution, etc. For an exhaustive list of new features see the official ODB 1.1.0 release announcement.

ODB - compiler-based ORM for C++

Wednesday, September 29th, 2010

If you have read my earlier posts on parsing C++ with GCC plugins (Part 1, Part 2, and Part 3), then you might remember that I mentioned a secret project that I have been working on. You might also have noticed that I’ve been neglecting this blog lately. Well, today is the day to unveil this secret project, which is what kept me busy for these past several months.

The project is called ODB and it is a compiler-based object-relational mapping (ORM) system for C++. It allows you to persist C++ objects to a relational database without having to deal with tables, columns, or SQL.

You might have already used other ORM implementations for C++. And if you have been exposed to ORM systems for other mainstream languages, such as Hibernate for Java, the C++ versions must have felt pretty inferior. The major sore point is the need to write some sort of serialization or registration code for each and every data member in each and every persistent class. Forgot to register a new member? Say good bye to your data.

The primary goal of the ODB project is to change that. It takes a different approach and uses a C++ compiler to parse your classes and automatically generate the database conversion code. Or, more precisely, it uses the new GCC plugin architecture to re-use the tried and tested GCC compiler frontend to parse C++. As a result, ODB is capable of handling any C++ code. While the ODB compiler uses GCC internally, its output is standard C++ which means that you can use any C++ compiler to build the generated code and your application.

Let’s see how a persistent class declaration will look in ODB:

  #pragma db object
  class person
  {
    ...
 
  private:
    friend class odb::access;
    person ();
 
    #pragma db id auto
    unsigned long id_;
 
    string first_;
    string last_;
    unsigned short age_;
  };

ODB is not a framework. It does not dictate how you should write your application. Rather, it is designed to fit into your style and architecture by only handling C++ object persistence and not interfering with any other functionality. As you can see, existing classes can be made persistent with only a few modifications.

Given the above class, we can perform various database operations with its objects:

  person john ("John", "Doe", 31);
  person jane ("Jane", "Doe", 29);
 
  transaction t (db.begin ());
 
  db.persist (john);
  db.persist (jane);
 
  result r (db.query<person> (query::age < 30));
  copy (r.begin (),
        r.end (),
        ostream_iterator<person> (cout, "n"));
 
  jane.age (jane.age () + 1);
  db.update (jane);
 
  t.commit ();

ODB is written in portable C++ and you should be able to use it with any modern C++ compiler. We have tested this release on GNU/Linux (x86/x86-64), Windows (x86/x86-64), Mac OS X, and Solaris (x86/x86-64/SPARC) with GNU g++ 4.2.x-4.5.x, MS Visual C++ 2008 and 2010, and Sun Studio 12. The dependency-free ODB compiler binaries are available for all of the above platforms. The initial release only supports MySQL as the underlying database. Support for other database systems is in the works.

Well, I hope this sounds as exciting to you as it does to me. And I hope you will enjoy playing around with ODB (check out the Hello World Example if nothing else) while I go catch up on some sleep.

Free proprietary license for XSD and XSD/e

Tuesday, August 3rd, 2010

Today we introduced a free proprietary license for CodeSynthesis XSD and XSD/e. The new license allows you to handle small XML vocabularies (less than 10,000 lines of generated code) in proprietary/closed-source applications free of charge and without any of the GPL restrictions such as having to publish your source code.

What were the reasons for offering such a license? After all, it seems like we will just loose money on this deal. We often get requests for our commercial proprietary license from developers that have a fairly small XML vocabulary. Typically a configuration file or a small communication protocol for their application. While the XML documents are quite simple and it wouldn’t be very hard to parse them using DOM or SAX, the developers would still prefer to handle this task using our tools. After all, spending a few days writing mind-numbing code is still worse than generating the same code in a few seconds.

However, the administrative burdens and delays involved in such a purchase (getting approval from management, contacting the purchasing department, purchasing via PO or credit card, etc.) are often hard to justify considering such simple XML processing needs. The administrative overheads on our side (processing the PO or credit card, delivering the license, issuing the invoice, etc.) also force us to set a minimum limit on the license size and price that we can offer.

All this usually leads to either the license being too expensive for the task at hand or the understandable unwillingness of the developers to endure the purchasing process. As a result we have decided to spare the developers the agony of using inferior products and/or raw XML processing APIs and offer this license for free.

How much is 10,000 lines of code? While it depends on the optional XSD and XSD/e compiler features that you use (e.g., support for XML serialization, polymorphism, comparison and printing operators, as well as XML Schema validation in case of XSD/e), as a rule of thumb, 10,000 lines of code are roughly equivalent to 40-50 local element/attribute definitions in the schema. This should be sufficient to handle small and and even some medium-sized XML vocabularies. Also, if you have your schemas ready, you can quickly check how much generated code they require by downloading XSD or XSD/e and passing the --show-sloc option when compiling the schemas.

For more information on the new license as well as for answers to other common questions, see the following pages: