ORM for C++: Comparison and Examples
In the world of C++, working with databases has historically been more low-level than in languages like Python or Java. However, modern projects require convenience and productivity, so developers are increasingly turning to ORM (Object-Relational Mapping). ORM for C++ allows you to work with tables as regular C++ objects, automatically generate SQL queries, and manage schema migrations. In this article, we will look at five popular libraries: ODB, SQLiteCpp, SOCI, cppdb, and sqlpp11, compare their features, and provide practical examples.
1. ODB — The Corporate Standard
ODB is a powerful ORM framework for C++ that supports many DBMS: MySQL, PostgreSQL, SQLite, Oracle, SQL Server. The main feature is code generation: you describe the model in a header file with annotations, and the ODB compiler (odb) creates the code for working with the database.
// person.hxx#pragma once#include <string>#include <odb/core.hxx>
#pragma db objectclass Person {public: Person() {} Person(const std::string& name, int age) : name_(name), age_(age) {}
const std::string& getName() const { return name_; } int getAge() const { return age_; }
private: friend class odb::access; #pragma db id auto unsigned long id_; std::string name_; int age_;};After running odb -d mysql person.hxx, files with the implementation are generated. Then we use the standard pattern:
#include <odb/database.hxx>#include <odb/mysql/database.hxx>#include "person-odb.hxx"
int main() { auto db = std::make_shared<odb::mysql::database>("user", "pass", "mydb"); Person p("Alice", 30); db->persist(p); // Loading auto loaded = db->load<Person>(1); return 0;}Pros: full ORM, migration support, lazy loading, transaction handling. Cons: complex setup, need for code generation, heavyweight for small projects.
2. SQLiteCpp — Lightweight and Simple
SQLiteCpp is a C++ RAII-style wrapper over SQLite3. It is not a full-fledged ORM, but it provides a convenient interface for working with queries.
#include <SQLiteCpp/Database.h>#include <SQLiteCpp/Statement.h>
int main() { SQLite::Database db("test.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); db.exec("CREATE TABLE IF NOT EXISTS person (id INTEGER PRIMARY KEY, name TEXT, age INT)"); SQLite::Statement insert(db, "INSERT INTO person (name, age) VALUES (?, ?)"); insert.bind(1, "Bob"); insert.bind(2, 25); insert.exec(); // Reading SQLite::Statement query(db, "SELECT * FROM person WHERE age > ?"); query.bind(1, 20); while (query.executeStep()) { std::cout << query.getColumn(1) << " " << query.getColumn(2) << ""; } return 0;}Pros: minimalistic, no code generation required, works only with SQLite. Cons: no object mapping, manual SQL writing.
3. SOCI — Universal Abstraction Layer
SOCI (The C++ Database Access Library) is not an ORM in the pure sense, but an abstraction over different DBMS (SQLite, PostgreSQL, MySQL, Oracle). It provides a convenient syntax for executing queries and binding results to C++ structures.
#include <soci/soci.h>#include <soci/sqlite3.h>
struct Person { int id; std::string name; int age;};
int main() { soci::session sql(soci::sqlite3, "test.db"); sql << "CREATE TABLE person (id INTEGER PRIMARY KEY, name TEXT, age INT)"; Person p{1, "Charlie", 35}; sql << "INSERT INTO person (id, name, age) VALUES (:id, :name
``` Also in library
JSON Processing in Swift: Codable, JSONSerialization and Practical Examples
Generating PDF Reports in Go: Library Overview and Code Examples
Geolocation and Maps in Kotlin: Overview of Libraries and Tools
Database Migrations in Java: Overview of Popular Tools
Geolocation and Maps in Kotlin: Libraries, Tools, and Examples