This pages shows some simple examples, most of things they demostrate are also shown on other pages.
Here's the schema:
<?xml version="1.0" encoding="UTF-8"?>
<sbe:messageSchema package="market" id="1" version="0" byteOrder="littleEndian">
<types>
<composite name="messageHeader">
<type name="blockLength" primitiveType="uint16"/>
<type name="templateId" primitiveType="uint16"/>
<type name="schemaId" primitiveType="uint16"/>
<type name="version" primitiveType="uint16"/>
</composite>
<composite name="groupSizeEncoding">
<type name="blockLength" primitiveType="uint16"/>
<type name="numInGroup" primitiveType="uint16"/>
</composite>
<composite name="varDataEncoding">
<type name="length" primitiveType="uint32"/>
<type name="varData" primitiveType="uint8" length="0"/>
</composite>
<type name="uint32_opt" primitiveType="uint32" presence="optional"/>
<enum name="numbers" encodingType="uint8">
<validValue name="One">1</validValue>
<validValue name="Two">2</validValue>
</enum>
<set name="options" encodingType="uint8">
<choice name="A">0</choice>
<choice name="B">2</choice>
</set>
</types>
<sbe:message name="msg" id="1">
<field name="field" id="1" type="uint32_opt"/>
<field name="number" id="2" type="numbers"/>
<field name="option" id="3" type="options"/>
<group name="group" id="4">
<field name="field" id="1" type="uint32"/>
</group>
<data name="data" id="5" type="varDataEncoding"/>
</sbe:message>
</sbe:messageSchema>
Encoding a message using normal accessors
std::array<char, 1024> buf{};
m.option(market::types::options{}.B(true));
m.field(3);
m.number(market::types::numbers::Two);
auto d = m.data();
d.assign_string("hi!");
auto g = m.group();
g.resize(2);
for(const auto entry : g)
{
entry.field(1);
}
constexpr View< Byte > make_view(Byte *ptr, const std::size_t size) noexcept
Constructs view from memory buffer.
Definition sbepp.hpp:4927
constexpr auto fill_message_header(Message m) noexcept -> decltype(m(detail::fill_message_header_tag{}))
Fills message header.
Definition sbepp.hpp:3998
constexpr auto fill_group_header(Group g, Size num_in_group) noexcept -> decltype(g(detail::fill_group_header_tag{}, num_in_group))
Fills group header.
Definition sbepp.hpp:4018
constexpr auto addressof(T v) noexcept -> decltype(v(detail::addressof_tag{}))
Returns pointer to the underlying data referenced by a view.
Definition sbepp.hpp:1631
constexpr std::size_t size_bytes(T v) noexcept
Returns the size of the underlying data represented by message/group/entry/data/composite view,...
Definition sbepp.hpp:1592
Encoding a message using cursor-based accessors
std::array<char, 1024> buf{};
m.field(1, c);
m.number(market::types::numbers::Two, c);
m.option(market::types::options{}.B(true), c);
auto g = m.group(c);
for(const auto entry : g.cursor_range(c))
{
entry.field(1, c);
}
d.assign_string("hi!");
constexpr detail::skip_cursor_wrapper< Byte > skip(cursor< Byte > &c) noexcept
Returns a wrapper which moves the cursor to the end of field/group/data without returning the accesse...
Definition sbepp.hpp:1740
constexpr detail::dont_move_cursor_wrapper< Byte > dont_move(cursor< Byte > &c) noexcept
Returns a wrapper which doesn't advance the cursor when it's used.
Definition sbepp.hpp:1706
constexpr cursor< byte_type_t< View > > init_cursor(View view) noexcept
Initializes cursor from a message/group view with the same byte type.
Definition sbepp.hpp:2858
Decoding a message using normal accessors
std::array<char, 1024> buf{};
buf.data(), buf.size());
if(*header.templateId()
{
std::cerr << "unknown message id: " << *header.templateId() << '\n';
return;
}
if(!checked_size.valid)
{
std::cerr << "bad message\n";
return;
}
auto d = m.data();
std::cout.write(d.data(), d.size());
const auto field = m.field();
if(field.has_value())
{
if(field.in_range())
{
std::cout << *field << '\n';
}
else
{
std::cout << "field value is out of range \n";
}
}
else
{
std::cout << "field is null\n";
}
std::cout << *m.option() << '\n';
auto g = m.group();
std::cout << "group size: " << g.size() << '\n';
for(const auto entry : g)
{
std::cout << *entry.field() << '\n';
}
static constexpr message_id_t id() noexcept
Returns id attribute.
HeaderComposite< Byte > header_type
Message header composite type.
Definition sbepp.hpp:4144
constexpr std::underlying_type< Enum >::type to_underlying(Enum e) noexcept
Converts an enumeration to its underlying type. Equivalent to C++23 std::to_underlying()
Definition sbepp.hpp:2839
constexpr size_bytes_checked_result size_bytes_checked(View view, std::size_t size) noexcept
Calculates view size with additional safety checks.
Definition sbepp.hpp:5634
constexpr View< typename std::add_const< Byte >::type > make_const_view(Byte *ptr, const std::size_t size) noexcept
Constructs read-only view from memory buffer.
Definition sbepp.hpp:4952
Decoding a message using cursor-based accessors
std::array<char, 1024> buf{};
{
std::cerr << "not a `market::messages::msg`" << '\n';
return;
}
const auto field = m.field(c);
if(field.has_value())
{
if(field.in_range())
{
std::cout << *field << '\n';
}
else
{
std::cout << "field value is out of range \n";
}
}
else
{
std::cout << "field is null\n";
}
std::cout << *m.option(c) << '\n';
auto g = m.group(c);
std::cout << "group size: " << g.size() << '\n';
for(const auto entry : g.cursor_range(c))
{
std::cout << *entry.field(c) << '\n';
}
auto d = m.data(c);
std::cout.write(d.data(), d.size());
constexpr auto get_header(T v) noexcept -> decltype(v(detail::get_header_tag{}))
Returns the header of a message/group.
Definition sbepp.hpp:1619
Decoding message header
There are couple of ways to decode message header from an incoming data.
Using sbepp::schema_traits::header_type
to get header type:
Using hardcoded header type(messageHeader
here):
And finally, when one knows for sure what schema they're working with, it's legal to create any message view from that schema and use sbepp::get_header()
to get the header. This works because header is the same for all messages within the schema. Of course access anything beyond the header still requires message type check.
Estimating buffer size to encode a message
It is possible to estimate how much memory is required to represent encoded message using sbepp::message_traits::size_bytes()
. To do this, one needs to know message structure details, the number of entries for each group and the total size of <data>
element(s) payload. Here's how it can be done at compile-time:
constexpr auto max_group_entries = 5;
constexpr auto max_data_size = 256;
market::schema::messages:msg>::size_bytes(
max_group_entries, max_data_size);
std::array<char, max_msg_size> buf{};
Provides various traits/attributes of a <message> element.
Definition sbepp.hpp:4352
or at run-time:
void make_msg(
const std::vector<std::uint32_t>& group_entries,
const std::vector<std::uint8_t>& data_payload)
{
market::schema::messages:msg>::size_bytes(
group_entries.size(), max_data_size.size());
std::vector<char> buf;
buf.resize(msg_size);
}
Stringification
Here's an example of how to build stringification visitor using fmt and Visit API (you can find the full example in stringification.test.cpp
):
class to_string_visitor
{
public:
template<typename T, typename Cursor, typename Tag>
void on_message(T m, Cursor& c, Tag)
{
append_line("content: ");
indentation++;
indentation--;
}
template<typename T, typename Cursor, typename Tag>
bool on_group(T g, Cursor& c, Tag)
{
indentation++;
indentation--;
return {};
}
template<typename T, typename Cursor>
bool on_entry(T entry, Cursor& c)
{
append_line("entry:");
indentation++;
indentation--;
return {};
}
template<typename T, typename Tag>
bool on_data(T d, Tag)
{
return {};
}
template<typename T, typename Tag>
bool on_field(T f, Tag)
{
return {};
}
template<typename T, typename Tag>
bool on_type(T t, Tag)
{
return {};
}
template<typename T, typename Tag>
bool on_enum(T e, Tag)
{
return {};
}
template<typename T, typename Tag>
bool on_set(T s, Tag)
{
return {};
}
template<typename T, typename Tag>
bool on_composite(T c, Tag)
{
return {};
}
template<typename Tag>
void on_enum_value(auto , Tag)
{
}
void on_enum_value(auto e, sbepp::unknown_enum_value_tag)
{
}
template<typename Tag>
void on_set_choice(const bool value, Tag)
{
if(value)
{
if(!is_first_choice)
{
append(", ");
}
is_first_choice = false;
}
}
const std::string& str() const
{
return res;
}
private:
std::string res;
std::size_t indentation{};
bool is_first_choice{};
void indent()
{
fmt::format_to(std::back_inserter(res), "{:{}}", "", indentation * 4);
}
template<typename... Args>
void append(fmt::format_string<Args...> fmt, Args&&... args)
{
fmt::format_to(
std::back_inserter(res), fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void append_line(fmt::format_string<Args...> fmt, Args&&... args)
{
indent();
append(fmt, std::forward<Args>(args)...);
res.push_back('\n');
}
void on_encoding(sbepp::required_type auto t, const char* name)
{
append_line("{}: {}", name, *t);
}
void on_encoding(sbepp::optional_type auto t, const char* name)
{
if(t)
{
append_line("{}: {}", name, *t);
}
else
{
append_line("{}: null", name);
}
}
void on_encoding(sbepp::array_type auto a, const char* name)
{
on_array(a, name);
}
template<typename T>
void on_array(T a, const char* name)
{
if constexpr(std::is_same_v<typename T::value_type, char>)
{
append_line("{}: {:.{}}", name, a.data(), a.size());
}
else
{
append_line("{}: {}", name, a);
}
}
void on_encoding(sbepp::enumeration auto e, const char* name)
{
indent();
append("{}: ", name);
}
void on_encoding(sbepp::set auto s, const char* name)
{
indent();
append("{}: (", name);
is_first_choice = true;
append(")\n");
}
void on_encoding(sbepp::composite auto c, const char* name)
{
append_line("{}:", name);
indentation++;
indentation--;
}
};
fmt::print("{}", res.str());
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
static constexpr const char * name() noexcept
Returns name attribute.
constexpr Visitor && visit_children(View view, Cursor &c, Visitor &&visitor={})
Visits view's children using provided cursor.
Definition sbepp.hpp:5369
constexpr Visitor && visit(View view, Cursor &c, Visitor &&visitor={})
Visits a view using given cursor.
Definition sbepp.hpp:5229
Now, for a message like:
<enum name="numbers_enum" encodingType="uint8">
<validValue name="One">1</validValue>
<validValue name="Two">2</validValue>
</enum>
<set name="options_set" encodingType="uint8">
<choice name="A">0</choice>
<choice name="B">2</choice>
</set>
<sbe:message name="msg28" id="28">
<field name="required" id="1" type="uint32"/>
<field name="optional1" id="2" type="uint32_opt"/>
<field name="optional2" id="3" type="uint32_opt"/>
<field name="number" id="4" type="numbers_enum"/>
<field name="option" id="5" type="options_set"/>
<field name="string" id="6" type="str128"/>
<field name="array" id="7" type="arr8"/>
<group name="group" id="8">
<field name="number" id="1" type="uint32"/>
</group>
<data name="varData" id="9" type="varDataEncoding"/>
<data name="varStr" id="10" type="varStrEncoding"/>
</sbe:message>
we can get the following output:
message: msg28
messageHeader:
blockLength: 150
templateId: 28
schemaId: 1
version: 0
content:
required: 1
optional1: 2
optional2: null
number: One
option: (A, B)
string: hi
array: [1, 0, 2, 0, 0, 0, 0, 0]
group:
entry:
number: 1
entry:
number: 2
varData: [1, 2]
varStr: ab
Automatic handling of all schema messages
Using sbepp::schema_traits::message_tags
we can make a helper that takes incoming message buffer, automatically detects what schema message it represents and passes to the callback a corresponding message view object (see the full example in handle_schema_message.test.cpp
):
template<typename SchemaTag, typename Byte, typename... MessageTags, typename F>
void handle_message_impl(
const Byte* data,
const std::size_t size,
F&& cb,
{
const auto msg_id =
.templateId()
.value();
const auto try_create_message = [msg_id, data, size, &cb]<typename Tag>(Tag)
{
{
return true;
}
return false;
};
const auto is_known_message = (try_create_message(MessageTags{}) || ...);
if(!is_known_message)
{
}
}
template<typename SchemaTag, typename Byte, typename F>
void handle_schema_message(const Byte* data, const std::size_t size, F&& cb)
{
handle_message_impl<SchemaTag>(
data, size, std::forward<F>(cb), message_tags{});
}
handle_schema_message<test_schema::schema>(
buf.data(), buf.size(), overloaded{
[](test_schema::messages::msg4<const byte_type>)
{
},
[](auto)
{
}
}
);
Provides various traits/attributes of a <messageSchema> element.
Definition sbepp.hpp:4124
sbepp::type_list< MessageTags... > message_tags
Schema message tags in schema order.
Definition sbepp.hpp:4150
An empty structure to represent a sequence of types.
Definition sbepp.hpp:378
With this helper, users can avoid writing separate if-else/switch-case statement per each schema message manually and only handle messages they are interested in.
Tag-based accessors
Access field by name
Sometimes it's useful to access fields by their names. Using sbepp::message_traits::field_tags
and sbepp::field_traits::name
it's possible to generate by-name accessors at compile-time (see the full example in get_by_name.test.cpp
):
std::uint64_t get_by_name_impl(
Message msg,
const std::string_view field_name,
{
std::uint64_t res{};
auto try_get_value = [&res]<typename Tag>(auto msg, auto name, Tag)
{
{
if constexpr(
{
}
{
res = static_cast<std::uint64_t>(
}
else
{
throw std::runtime_error{"Unsupported field type"};
}
return true;
}
return false;
};
const auto is_found = (try_get_value(msg, field_name, FieldTags{}) || ...);
if(!is_found)
{
throw std::runtime_error{"Wrong field name"};
}
return res;
}
template<sbepp::message Message>
std::uint64_t get_by_name(Message msg, const std::string_view field_name)
{
return get_by_name_impl(
msg,
field_name,
}
auto value = get_by_name(msg, "fieldName");
TypeTag value_type_tag
value_type's tag. Not available for constants of numeric types
Definition sbepp.hpp:4511
Concept for sbepp::is_enum_tag<Tag>::value
Definition sbepp.hpp:5879
Concept for sbepp::is_message<T>::value
Definition sbepp.hpp:5184
Concept for sbepp::is_set_tag<Tag>::value
Definition sbepp.hpp:5887
Concept for sbepp::is_type_tag<Tag>::value
Definition sbepp.hpp:5875
typename traits_tag< ValueType >::type traits_tag_t
Shorthand for sbepp::traits_tag<T>::type
Definition sbepp.hpp:4677
constexpr auto get_by_tag(ViewOrSet viewOrSet) noexcept -> decltype(viewOrSet(detail::access_by_tag_tag{}, Tag{}))
Gets field or set choice value by tag.
Definition sbepp.hpp:5661
Set all optional fields to null
When creating a new SBE message, underlying memory buffer is often initialized with zeros. This is OK for required
fields which by definition must be set explicitly. However, optional
fields are different and zeros might not represent their null values (in fact, none of the SBE built-in types except char
have 0
as their null value). To avoid setting each of them manually, we can iterate over message field tags, check their presence
and set optional
ones to null (see the full example in nullify_optional_fields.test.cpp
):
template<typename... FieldTags>
{
auto set_nullopt = []<typename Tag>(auto view, Tag)
{
if constexpr(
{
}
};
(set_nullopt(view, FieldTags{}), ...);
}
template<sbepp::message Message>
void nullify_optional_fields(Message msg)
{
nullify_optional_fields_impl(
msg,
}
nullify_optional_fields(msg);
static constexpr field_presence presence() noexcept
Returns the actual presence. Note that it can be different from the one provided in schema,...
constexpr nullopt_t nullopt
Helper constant used to initialize optional types with null value.
Definition sbepp.hpp:3725
@ optional
field is optional
Definition sbepp.hpp:317
constexpr auto set_by_tag(ViewOrSet &&viewOrSet, Value &&value) noexcept -> decltype(std::forward< ViewOrSet >(viewOrSet)(detail::access_by_tag_tag{}, Tag{}, std::forward< Value >(value)))
Sets field or set choice value by tag.
Definition sbepp.hpp:5692