ores.cpp.wire-codec.codec_header
Table of Contents
Declares a type's four wire functions — to_json, from_json, to_msgpack,
from_msgpack — and the ores::utility::codec::wire_traits specialisation that
names them.
The specialisation has to be visible in every translation unit that names the trait for the type, because a template argument the compiler cannot see here is resolved against the primary template there, and two translations of one type disagreeing about its encoding is the one thing the seam exists to prevent. So the specialisation is stated in a header, and this is it.
A junction side that sets :enrich_code: carries the joined row's code in a
member the table has no column for, filled by the repository's enrichment
query and declared after the audit members. That member is on the wire too, so
the junction's field list ends with one entry per enriched side, named as the
repository names it.
See the Template variable reference for the complete list of available variables and their semantics.
1. Template
The full template source. Edit here and re-tangle with
compass build --direct tangle_codegen_templates to regenerate
library/templates/cpp_domain_type_codec.hpp.mustache.
{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/ores.cpp.wire-codec.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#ifndef ORES_{{component_include_upper}}_DOMAIN_{{entity_singular_upper}}_CODEC_HPP
#define ORES_{{component_include_upper}}_DOMAIN_{{entity_singular_upper}}_CODEC_HPP
#include "ores.{{component_include}}/domain/{{entity_singular}}.hpp"
#include "ores.{{component_include}}/export.hpp"
#include "ores.utility/codec/wire_fields.hpp"
#include "ores.utility/codec/wire_traits_fwd.hpp"
{{#wire_codec_includes}}
#include "{{{.}}}"
{{/wire_codec_includes}}
#include <cstddef>
#include <span>
#include <string>
#include <string_view>
#include <tuple>
namespace ores::{{component}}::domain {
{{#has_domain_groups}}
/**
* @brief The members of {{entity_singular}}, named as the wire names them and
* in the order the type declares them.
*
* This is the one statement of the type's shape. The carriers find it by
* argument-dependent lookup, so no caller passes a list, and a message whose
* member is this type encodes it as its own object without knowing the type.
*/
[[nodiscard]] constexpr auto wire_fields(std::type_identity<{{entity_singular}}>) {
return std::tuple{
{{#domain_groups}} utility::codec::field{"{{member}}", &{{entity_singular}}::{{member}}},
{{/domain_groups}} };
}
{{/has_domain_groups}}
{{^has_domain_groups}}
/**
* @brief The members of {{entity_singular}}, named as the wire names them and
* in the order the type declares them.
*
* This is the one statement of the type's shape. The carriers find it by
* argument-dependent lookup, so no caller passes a list, and a message whose
* member is this type encodes it as its own object without knowing the type.
*/
[[nodiscard]] constexpr auto wire_fields(std::type_identity<{{entity_singular}}>) {
return std::tuple{
{{#has_identity_group}} utility::codec::field{"identity", &{{entity_singular}}::identity},
{{/has_identity_group}}
{{^has_identity_group}}
{{#has_audit_columns}} utility::codec::field{"version", &{{entity_singular}}::version},
{{/has_audit_columns}}
{{#has_tenant_id}} utility::codec::field{"tenant_id", &{{entity_singular}}::tenant_id},
{{/has_tenant_id}}
{{#primary_key.columns}} utility::codec::field{"{{column}}", &{{entity_singular}}::{{column}}},
{{/primary_key.columns}}
{{#natural_keys}} utility::codec::field{"{{column}}", &{{entity_singular}}::{{column}}},
{{/natural_keys}}
{{/has_identity_group}}
{{#columns}}
{{^is_identity_group_column}}
{{^sql_only}}
{{^no_wire}} utility::codec::field{"{{name}}", &{{entity_singular}}::{{name}}},
{{/no_wire}}
{{/sql_only}}
{{/is_identity_group_column}}
{{/columns}}
{{#has_audit_group}} utility::codec::field{"audit", &{{entity_singular}}::audit},
{{/has_audit_group}}
{{^has_audit_group}}
{{#has_audit_columns}} utility::codec::field{"modified_by", &{{entity_singular}}::modified_by},
utility::codec::field{"performed_by", &{{entity_singular}}::performed_by},
utility::codec::field{"change_reason_code", &{{entity_singular}}::change_reason_code},
utility::codec::field{"change_commentary", &{{entity_singular}}::change_commentary},
utility::codec::field{"recorded_at", &{{entity_singular}}::recorded_at},
{{/has_audit_columns}}
{{/has_audit_group}}
};
}
{{/has_domain_groups}}
}
namespace ores::utility::codec {
/**
* @brief The generated codec: what the wire codec resolves to for this type.
*
* The functions are declared here and defined in this type's own translation
* unit, so a translation unit that only sends the type compiles a declaration
* rather than a second copy of the encoder.
*/
template <>
struct ORES_{{component_include_upper}}_EXPORT
wire_traits<ores::{{component}}::domain::{{entity_singular}}> {
/**
* @brief States that this is a generated codec.
*
* A test asserts this rather than the encoding, because the encoding is
* what the type's own case already round-trips: what it
* cannot see is whether the seam resolved to this specialisation or fell
* through to the primary template, and a marker that only this
* specialisation declares is that answer at compile time.
*/
static constexpr bool is_generated = true;
static std::string write_json(const ores::{{component}}::domain::{{entity_singular}}& v);
static ores::{{component}}::domain::{{entity_singular}} read_json(std::string_view text);
static std::string write_msgpack(const ores::{{component}}::domain::{{entity_singular}}& v);
static ores::{{component}}::domain::{{entity_singular}} read_msgpack(std::span<const std::byte> data);
};
}
#endif
{{/domain_entity}}
{{#junction}}
#ifndef ORES_{{component_include_upper}}_DOMAIN_{{name_singular_upper}}_CODEC_HPP
#define ORES_{{component_include_upper}}_DOMAIN_{{name_singular_upper}}_CODEC_HPP
#include "ores.{{component_include}}/domain/{{name_singular}}.hpp"
#include "ores.{{component_include}}/export.hpp"
#include "ores.utility/codec/wire_fields.hpp"
#include "ores.utility/codec/wire_traits_fwd.hpp"
#include <cstddef>
#include <span>
#include <string>
#include <string_view>
#include <tuple>
namespace ores::{{component}}::domain {
/**
* @brief The members of {{name_singular}}, named as the wire names them and
* in the order the type declares them.
*
* This is the one statement of the type's shape. The carriers find it by
* argument-dependent lookup, so no caller passes a list, and a message whose
* member is this type encodes it as its own object without knowing the type.
*/
[[nodiscard]] constexpr auto wire_fields(std::type_identity<{{name_singular}}>) {
return std::tuple{
utility::codec::field{"version", &{{name_singular}}::version},
{{#has_tenant_id}} utility::codec::field{"tenant_id", &{{name_singular}}::tenant_id},
{{/has_tenant_id}}
utility::codec::field{"{{left.column}}", &{{name_singular}}::{{left.column}}},
utility::codec::field{"{{right.column}}", &{{name_singular}}::{{right.column}}},
{{#columns}} utility::codec::field{"{{name}}", &{{name_singular}}::{{name}}},
{{/columns}}
utility::codec::field{"modified_by", &{{name_singular}}::modified_by},
utility::codec::field{"performed_by", &{{name_singular}}::performed_by},
utility::codec::field{"change_reason_code", &{{name_singular}}::change_reason_code},
utility::codec::field{"change_commentary", &{{name_singular}}::change_commentary},
utility::codec::field{"recorded_at", &{{name_singular}}::recorded_at},
{{#left.enrich_code}} utility::codec::field{"{{left.column_short}}_code", &{{name_singular}}::{{left.column_short}}_code},
{{/left.enrich_code}}
{{#right.enrich_code}} utility::codec::field{"{{right.column_short}}_code", &{{name_singular}}::{{right.column_short}}_code},
{{/right.enrich_code}}
};
}
}
namespace ores::utility::codec {
/**
* @brief The generated codec: what the wire codec resolves to for this type.
*
* The functions are declared here and defined in this type's own translation
* unit, so a translation unit that only sends the type compiles a declaration
* rather than a second copy of the encoder.
*/
template <>
struct ORES_{{component_include_upper}}_EXPORT
wire_traits<ores::{{component}}::domain::{{name_singular}}> {
/**
* @brief States that this is a generated codec.
*
* A test asserts this rather than the encoding, because the encoding is
* what the type's own case already round-trips: what it
* cannot see is whether the seam resolved to this specialisation or fell
* through to the primary template, and a marker that only this
* specialisation declares is that answer at compile time.
*/
static constexpr bool is_generated = true;
static std::string write_json(const ores::{{component}}::domain::{{name_singular}}& v);
static ores::{{component}}::domain::{{name_singular}} read_json(std::string_view text);
static std::string write_msgpack(const ores::{{component}}::domain::{{name_singular}}& v);
static ores::{{component}}::domain::{{name_singular}} read_msgpack(std::span<const std::byte> data);
};
}
#endif
{{/junction}}
2. See also
- Parent facet: ores.cpp.wire-codec
- ores.cpp.wire-codec.codec_impl — the definitions this header declares.