This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Report: using GCC plugin to implement ORM for C++
- From: Boris Kolpackov <boris at codesynthesis dot com>
- To: gcc at gcc dot gnu dot org
- Date: Thu, 30 Sep 2010 10:26:33 +0000 (UTC)
- Subject: Report: using GCC plugin to implement ORM for C++
Hi,
We have just released a C++ object-relational mapping (ORM) system,
called ODB, that uses the new GCC plugin architecture. I thought I
would report back to the GCC community on how the plugin part worked
out.
In a nutshell, the ODB compiler parses a C++ header with class
declarations (and some custom #pragma's that control the mapping)
and generates C++ code that performs the conversion between these
classes and their database representation. I have included some more
information on ODB at the end of this email for those interested.
What worked well:
- Access to pretty much all of the GCC functions, macros, and
internal data structures.
- Ability to register custom pragmas and attributes.
- The tree contains information about typedef aliases. Given the
following code:
class c {};
typedef c c_t;
c_t x;
One can discover that 'x' was declared as 'c_t', not just that its
type is 'c'. This is very useful when performing the source-to-source
translation.
What didn't work so well:
- The plugin header inclusion is a mess. You have to include the right
set of headers in the right order to get things to compile. Plus, the
GCC headers poison some declarations, so, for example, you cannot use
std::abort. Maybe there is a good reason for this.
- If the plugin needs to generate something other than assembly (C++
in our case), then the code that suppresses the opening of the
assembly file is quite hackish.
- There is no callback point after the tree has been constructed and
before any other transformations have been performed. We use the
first gate callback (PLUGIN_OVERRIDE_GATE) and do all our work
there. There is also no well-defined way to stop the compilation
process at this point. We simply use exit().
- Working with the GCC tree is not for the faint of heart. Generating
code from it is particularly hard. In fact, we have created our own
C++ classes (called "semantics graph") to represent the translation
unit being compiled. It is not as complete as the GCC's tree but it
is a lot easier to traverse.
We have built and tested the ODB plugin with GCC 4.5.1 on the following
platforms:
GNU/Linux
Predictably, everything works out of the box.
Solaris
Had to add OBJDUMP=gobjdump (or /usr/sfw/bin/gobjdump) when configuring
GCC. Otherwise, the -rdynamic test will fail. Tested both x86 and SPARC.
Mac OS X
Had to apply a backported patch for PR 43715.
Windows
Well, this one was fun. There is no dlopen/dlsym so no plugin support.
What we did was this: we linked in the ODB plugin statically to the
cc1plus binary. This required a small patch to the GCC plugin loading
code. The patch is small instead of being large thanks to the way the
plugin support is implemented in GCC. Even if plugin support is not
enabled, most of the code (callback points, etc.) are still there. It's
just there is no way to load a plugin. This way it was fairly straight-
forward to add another method of "loading" a plugin. Kudos to whoever
designed this. I can share the patch/build instructions with anyone
interested.
Overall, I think, the GCC plugin architecture is a great addition. The
amount of flexibility it affords you is quite amazing.
Some more information on ODB:
ODB is an open-source, 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 and
without manually writing any mapping code. For example:
#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::last == "Doe" && query::age < 30));
copy (r.begin (), r.end (), ostream_iterator<person> (cout, "\n"));
jane.age (jane.age () + 1);
db.update (jane);
t.commit ();
The ODB compiler uses the GCC compiler frontend for C++ parsing and is
implemented using the new GCC plugin architecture. While ODB uses GCC
internally, its output is standard C++ which means that you can use
any C++ compiler to build your application.
ODB is written in portable C++ and you should be able to use it with
any modern C++ compiler. In particular, 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
supports MySQL as the underlying database. Support for other database
systems is in the works.
More information, documentation, source code, and pre-compiled binaries
are available from:
http://www.codesynthesis.com/products/odb/
Boris