A modern, header-only C++ SQLite3 wrapper, developed and proven here in isolation,
then embedded — wholesale — into the jac313 monorepo as
jac313::Qlite::vNNN. Standalone, Qlite lives under Qlite::vNNN; it depends only on
system SQLite3.
Qlite is the canonical home of this wrapper. It is the evolution of the legacy
jacQlite/jac::qliterepo, which is being retired. The library is developed and tested standalone here, and a copy is forward-ported into jac313 — jac313 never build-depends on this repo (see Standalone + embed).
TBD — to be filled. The reason to carry a non-trivial, two-standard wrapper across
real compilers is to make toolchains misbehave where hello-world never will. When a
minimal reproducer earns its place, it lands in vNNN/docs/findings/ and is written up
here.
| Compiler | -std=c++23 |
-std=c++26 |
|---|---|---|
| gcc 15.x | TBD | TBD |
| clang 21.x | TBD | TBD |
See v001/docs/findings/ and v002/docs/findings/.
Qlite mirrors jac313's shape: an umbrella over two self-contained version worlds. They are fully duplicated — DRY is not applied across them — so each can move its toolchain and language standard independently and testing stays isolated (changing v001 only tests v001).
| Version | Standard | State |
|---|---|---|
| v001/ | C++23 | The proven baseline — the Qlite::v001 wrapper + Setup::v001 toolchain sensing, textual headers and an optional C++23 module. Status TBD. |
| v002/ | C++26 | The frontier — Qlite::v002 + Setup::v002, using real C++26 (a contracts pre/post/assert shim, etc.). See v002/docs/Cpp26_Adoption.md. Status TBD. |
Each world contains the same parts: the Qlite wrapper component, a Setup component
(toolchain sensing), a bootstrap.sh, a docs/ tree, and (eventually) a
Qlite_test_cli compiled test runner.
Over the legacy jacQlite API, Qlite adds four features — all additive; the
frozen surface that downstreams depend on keeps working (see
Frozen surface).
| # | Feature | One line |
|---|---|---|
| 1 | RAII transaction guard | auto tx = db.transaction(); … tx.commit(); else auto-rollback on scope exit — kills the forgot-to-rollback bug class. |
| 2 | Prepared-statement cache | db.prepare_cached(sql) returns a reusable cached Statement&. |
| 3 | Ergonomics | string_view / std::span<const std::byte> (blob) binds, column-by-name, named-parameter binds (bind_param(":name", v)), get_all<Ts...>(). |
| 4 | std::expected non-throwing path |
try_exec / try_step (guarded by __cpp_lib_expected), alongside the throwing API. |
Per-feature detail lives in each world's docs/qlite/:
binding ·
transactions ·
persistence ·
errors.
These symbols are what jac313's Store SQL sink depends on and must keep working:
Sqlite ctor, exec, exec(sql, args...), begin/commit/rollback, prepare, and
Statement's bind, step, reset, clear_bindings, bind_int64, bind_double,
get, get_row, plus column<T>, bind_value, SqliteError::code(), and version().
The four features above never break this surface.
This is the core philosophy, derived from real decisions:
- Standalone + embed, never depend. Qlite is developed and proven in isolation here, then a copy is forward-ported into jac313. jac313 never build-depends on this repo — it embeds a vendored copy. Rationale: the "outside-dependency trap" — moved tags, missing checkouts, upstream churn — is what breaks builds. A vendored copy can't be broken by upstream.
- Self-contained version worlds. v001 and v002 are fully duplicated; DRY is deliberately not applied across versions, so each can move its toolchain/standard independently and testing stays isolated.
- Bring it all in, wholesale. When embedding into jac313, bring the complete self-contained world (even unused parts) — one integration event, not N. Unused-but- compiling code is cheap; a missing piece discovered later is expensive.
- Namespace rule. Standalone is jac313 minus the
jac313::umbrella:Qlite::vNNN↔jac313::Qlite::vNNN;Setup::vNNN↔jac313::Setup::vNNN. The forward-port simply prependsjac313::. - Shared test runner as anti-drift. The same
Qlite_test_cli+ same test suite runs in both this repo and inside jac313 per version, which proves the embedded copy hasn't drifted.
The full story is in each world's Architecture.md (umbrella/version-world model + the forward-port-to-jac313 narrative).
#include <Qlite/v001.hpp>
Qlite::v001::Sqlite db("app.db");
db.exec("CREATE TABLE IF NOT EXISTS events (id INTEGER, payload TEXT)");
db.exec("INSERT INTO events VALUES (?, ?)", 1, std::string("hello"));cd v001
./bootstrap.sh # sense toolchain → build the test runner → hand offAlways work from inside a version directory — build trees and results stay anchored to the version root, which is what keeps the worlds isolated. C++23 (v001) or C++26 (v002) is required; full prerequisites are in each world's docs/Setup.md.
Qlite/
├── README.md # this file
├── LICENSE # governs both versions
├── v001/ # complete C++23 world (Qlite wrapper, Setup, docs, tests)
└── v002/ # complete C++26 world (Qlite wrapper, Setup, docs, tests)
Each vNNN/ contains:
vNNN/
├── README.md # the version-world overview
├── bootstrap.sh # sense host → build the test runner → hand off (owned elsewhere)
├── Qlite/ # the wrapper component (namespace Qlite::vNNN)
├── Setup/ # toolchain sensing (namespace Setup::vNNN)
└── docs/ # this world's documentation set
LICENSE governs both versions. This is reference/learning software, provided "AS IS" without warranty; see the license for the governing terms.