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
Construct view from memory buffer.
Definition sbepp.hpp:4893
constexpr auto fill_message_header(Message m) noexcept -> decltype(m(detail::fill_message_header_tag{}))
Fill message header.
Definition sbepp.hpp:3986
constexpr auto fill_group_header(Group g, Size num_in_group) noexcept -> decltype(g(detail::fill_group_header_tag{}, num_in_group))
Fill group header.
Definition sbepp.hpp:4006
constexpr auto addressof(T v) noexcept -> decltype(v(detail::addressof_tag{}))
Returns pointer to the underlying data referenced by a view.
Definition sbepp.hpp:1619
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:1580
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:1728
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:1694
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:2846
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:4132
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:2827
constexpr size_bytes_checked_result size_bytes_checked(View view, std::size_t size) noexcept
Calculate view size with additional safety checks.
Definition sbepp.hpp:5522
constexpr View< typename std::add_const< Byte >::type > make_const_view(Byte *ptr, const std::size_t size) noexcept
Construct read-only view from memory buffer.
Definition sbepp.hpp:4918
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:1607
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:4330
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={})
Visit view's children using provided cursor.
Definition sbepp.hpp:5297
constexpr Visitor && visit(View view, Cursor &c, Visitor &&visitor={})
Visit a view using given cursor.
Definition sbepp.hpp:5195
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