Models
Models are ordinary aggregate structs. Reflect reads table metadata from C++26 static reflection and field annotations.
struct [[= reflect::table{"posts"}]] Post
{
[[= reflect::id, = reflect::auto_increment]]
std::int64_t id = 0;
[[= reflect::indexed, = reflect::not_null, = reflect::references{"users", "id", "CASCADE"}]]
std::int64_t user_id = 0;
[[= reflect::not_null, = reflect::varchar{200}]]
std::string title;
[[= reflect::nullable, = reflect::text]]
std::optional<std::string> summary;
[[= reflect::json, = reflect::not_null, = reflect::default_value{"'{}'"}]]
std::string metadata_json = "{}";
};
Table And Column Names
[[= reflect::table{"users"}]]
[[= reflect::column{"email_address"}]]
Without explicit names, Reflect uses the C++ type and field names.
Annotation strings are compile-time values with a 256-character maximum.
Aliases:
reflect::mapis an alias forreflect::columnreflect::db_typeis an alias forreflect::sql_typereflect::default_sqlis an alias forreflect::default_valuereflect::numericis an alias forreflect::decimalreflect::indexis an alias forreflect::indexedreflect::ignoredis an alias forreflect::ignore
Keys And Indexes
[[= reflect::id]]
[[= reflect::auto_increment]]
[[= reflect::unique]]
[[= reflect::indexed]]
reflect::id is an alias for reflect::primary_key.
Nullability
std::optional<T> fields are nullable by default. Non-optional fields are NOT NULL by default.
[[= reflect::nullable]]
std::optional<std::string> bio;
[[= reflect::not_null]]
std::string email;
Types
Reflect supports booleans, integral types, floating-point types, enums, std::string, byte vectors, chrono dates/times/timestamps, and std::optional<T> for those field types.
Common type annotations:
[[= reflect::varchar{320}]]
[[= reflect::text]]
[[= reflect::json]]
[[= reflect::uuid]]
[[= reflect::blob]]
[[= reflect::decimal{12, 2}]]
[[= reflect::date]]
[[= reflect::time]]
[[= reflect::timestamp]]
Use reflect::sql_type{"..."} when you need a backend-specific declaration:
[[= reflect::sql_type{"CITEXT"}]]
std::string email;
Defaults And Checks
Defaults and checks are raw SQL fragments:
[[= reflect::default_value{"'draft'"}]]
std::string status = "draft";
[[= reflect::check{"total >= 0"}]]
double total = 0.0;
Reflect does not quote or sanitize these fragments. They are part of the schema, not user input.
Created And Updated Timestamps
[[= reflect::created_at, = reflect::timestamp]]
std::chrono::system_clock::time_point created_at{};
[[= reflect::updated_at, = reflect::timestamp]]
std::chrono::system_clock::time_point updated_at{};
created_at and updated_at get a CURRENT_TIMESTAMP default. Both are skipped on insert. On update, updated_at is set by SQL rather than by the C++ object.
Foreign Keys
[[= reflect::references{"users", "id", "CASCADE"}]]
std::int64_t user_id = 0;
The positional fields are:
- referenced table
- referenced column
ON DELETEactionON UPDATEaction
Use the fourth value only when updates to the referenced key should cascade too.
Ignored Fields
[[= reflect::ignore]]
std::string cached_display_label;
Ignored fields are omitted from descriptors, DDL, inserts, updates, selects, and row materialization.
Model Rules
Reflect validates model shape at compile time:
- models must be default-initializable
- models must have at least one reflected non-static data member
- at least one persisted field is required
- there must be at most one primary key
- if no primary key annotation exists, a persisted field named
idbecomes the implicit primary key - primary keys cannot be
std::optional<T> auto_incrementrequires a non-optional integral field, excludingbool- temporal annotations require compatible chrono or string storage