sbepp
Loading...
Searching...
No Matches
Examples

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

// note: it's up to client to provide sufficient buffer
std::array<char, 1024> buf{};
auto m = sbepp::make_view<market::messages::msg>(buf.data(), buf.size());
// note: order doesn't matter, it's mixed for demo purpose
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);
}
// note: size_bytes is O(1) in this case because group is flat
const auto msg_size = sbepp::size_bytes(m);
send(sbepp::addressof(m), msg_size);
constexpr auto fill_message_header(Message m) noexcept -> decltype(m(detail::fill_message_header_tag{}))
Fill message header.
Definition sbepp.hpp:3997
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:4017
constexpr auto addressof(T v) noexcept -> decltype(v(detail::addressof_tag{}))
Returns pointer to the underlying data referenced by a view.
Definition sbepp.hpp:1626
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:1587

Encoding a message using cursor-based accessors

// note: it's up to client to provide sufficient buffer
std::array<char, 1024> buf{};
auto m = sbepp::make_view<market::messages::msg>(buf.data(), buf.size());
auto c = sbepp::init_cursor(m);
// note: order must be preserved for cursor-based accessors
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);
}
// note: don't move cursor yet
auto d = m.data(sbepp::cursor_ops::dont_move(c));
d.assign_string("hi!");
// ok, now just skip it
// size_bytes here is a no-op because cursor is already at the end
const auto msg_size = sbepp::size_bytes(m, c);
send(sbepp::addressof(m), msg_size);
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:1735
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:1701
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:2853

Decoding a message using normal accessors

std::array<char, 1024> buf{};
auto header = sbepp::make_const_view<
buf.data(), buf.size());
if(*header.templateId()
{
std::cerr << "unknown message id: " << *header.templateId() << '\n';
return;
}
auto m = sbepp::make_const_view<market::messages::msg>(buf.data(), buf.size());
// let's pretend we got this buffer from an untrusted network and want to be
// sure that the message is fully contained within given buffer.
auto checked_size = sbepp::size_bytes_checked(m);
if(!checked_size.valid)
{
std::cerr << "bad message\n";
return;
}
// note: order doesn't matter, it's mixed for demo purpose
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';
std::cout << sbepp::to_underlying(m.number()) << '\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:4143
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:2834
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:5533
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:4929

Decoding a message using cursor-based accessors

std::array<char, 1024> buf{};
auto m = sbepp::make_const_view<market::messages::msg>(buf.data(), buf.size());
// there's nothing wrong in creation of a "wrong" message view
if(*sbepp::get_header(m).templateId()
{
std::cerr << "not a `market::messages::msg`" << '\n';
return;
}
auto c = sbepp::init_cursor(m);
// note: order must be preserved for cursor-based accessors
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 << sbepp::to_underlying(m.number(c)) << '\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());
Provides various traits/attributes of a <message> element.
Definition sbepp.hpp:4341
constexpr auto get_header(T v) noexcept -> decltype(v(detail::get_header_tag{}))
Returns the header of a message/group.
Definition sbepp.hpp:1614

Decoding message header

There are couple of ways to decode message header from an incoming data.

  1. Using sbepp::schema_traits::header_type to get header type:

    // `make_view` works as well
    auto header = sbepp::make_const_view<
    buf.data(), buf.size());
  2. Using hardcoded header type(messageHeader here):

    auto header = sbepp::make_const_view<market::types::messageHeader>(
    buf.data(), buf.size());
  3. 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.

    // works even if `buf` represents a different message from `market` schema
    auto m = sbepp::make_const_view<market::messages::msg>(
    buf.data(), buf.size());
    auto header = sbepp::get_header(m);

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:

// specify max parameters
constexpr auto max_group_entries = 5;
constexpr auto max_data_size = 256;
constexpr auto max_msg_size = sbepp::message_traits<
market::schema::messages:msg>::size_bytes(
max_group_entries, max_data_size);
std::array<char, max_msg_size> buf{};
auto m = sbepp::make_view<market::messages::msg>(buf.data(), buf.size());
// encode message as usual but be sure not to exceed `max_group_entries` and
// `max_data_size`

or at run-time:

void make_msg(
const std::vector<std::uint32_t>& group_entries,
const std::vector<std::uint8_t>& data_payload)
{
// calculate buffer size required to store `group_entries` in `msg::group`
// and `data_payload` in `msg::data`
const auto msg_size = sbepp::message_traits<
market::schema::messages:msg>::size_bytes(
group_entries.size(), max_data_size.size());
std::vector<char> buf;
buf.resize(msg_size);
auto m = sbepp::make_view<market::messages::msg>(buf.data(), buf.size());
// encode message as usual...
}

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("message: {}", sbepp::message_traits<Tag>::name());
append_line("content: ");
indentation++;
sbepp::visit_children(m, c, *this);
indentation--;
}
template<typename T, typename Cursor, typename Tag>
bool on_group(T g, Cursor& c, Tag)
{
append_line("{}:", sbepp::group_traits<Tag>::name());
indentation++;
sbepp::visit_children(g, c, *this);
indentation--;
return {};
}
template<typename T, typename Cursor>
bool on_entry(T entry, Cursor& c)
{
append_line("entry:");
indentation++;
sbepp::visit_children(entry, c, *this);
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)
{
on_encoding(f, sbepp::field_traits<Tag>::name());
return {};
}
template<typename T, typename Tag>
bool on_type(T t, Tag)
{
on_encoding(t, sbepp::type_traits<Tag>::name());
return {};
}
template<typename T, typename Tag>
bool on_enum(T e, Tag)
{
on_encoding(e, sbepp::enum_traits<Tag>::name());
return {};
}
template<typename T, typename Tag>
bool on_set(T s, Tag)
{
on_encoding(s, sbepp::set_traits<Tag>::name());
return {};
}
template<typename T, typename Tag>
bool on_composite(T c, Tag)
{
return {};
}
template<typename Tag>
void on_enum_value(auto /*e*/, Tag)
{
}
void on_enum_value(auto e, sbepp::unknown_enum_value_tag)
{
append("unknown({})\n", sbepp::to_underlying(e));
}
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>)
{
// output char arrays as C-strings. Keep in mind that they are not
// required to be null-terminated so pass size explicitly
append_line("{}: {:.{}}", name, a.data(), a.size());
}
else
{
// use standard range-formatter
append_line("{}: {}", name, a);
}
}
void on_encoding(sbepp::enumeration auto e, const char* name)
{
indent();
append("{}: ", name);
sbepp::visit(e, *this);
}
void on_encoding(sbepp::set auto s, const char* name)
{
indent();
append("{}: (", name);
is_first_choice = true;
sbepp::visit(s, *this);
append(")\n");
}
void on_encoding(sbepp::composite auto c, const char* name)
{
append_line("{}:", name);
indentation++;
indentation--;
}
};
// usage:
auto res = sbepp::visit<to_string_visitor>(message);
fmt::print("{}", res.str());
Provides various traits/attributes of a <composite> element.
Definition sbepp.hpp:4297
Provides various traits/attributes of a <data> element.
Definition sbepp.hpp:4582
Provides various traits/attributes of an <enum> element.
Definition sbepp.hpp:4163
Provides various traits/attributes of a <validValue> element.
Definition sbepp.hpp:4200
Provides various traits/attributes of a <field> element.
Definition sbepp.hpp:4463
Provides various traits/attributes of a <group> element.
Definition sbepp.hpp:4512
Provides various traits/attributes of a <choice> element.
Definition sbepp.hpp:4267
Provides various traits/attributes of a <set> element.
Definition sbepp.hpp:4230
Provides various traits and attributes of a <type> element.
Definition sbepp.hpp:4042
Concept for sbepp::is_array_type<T>::value
Definition sbepp.hpp:5129
Concept for sbepp::is_composite<T>::value
Definition sbepp.hpp:5157
Concept for sbepp::is_enum<T>::value
Definition sbepp.hpp:5149
Concept for sbepp::is_optional_type<T>::value
Definition sbepp.hpp:5137
Concept for sbepp::is_required_type<T>::value
Definition sbepp.hpp:5133
Concept for sbepp::is_set<T>::value
Definition sbepp.hpp:5153
constexpr Visitor && visit_children(View view, Cursor &c, Visitor &&visitor={})
Visit view's children using provided cursor.
Definition sbepp.hpp:5308
constexpr Visitor && visit(View view, Cursor &c, Visitor &&visitor={})
Visit a view using given cursor.
Definition sbepp.hpp:5206
Tag for unknown enum values.
Definition sbepp.hpp:4941

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