Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement visit() for sets and enums #48

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -2404,7 +2404,8 @@ PREDEFINED = SBEPP_DOXYGEN \
SBEPP_HAS_INLINE_VARS=1 \
SBEPP_HAS_CONCEPTS=1 \
SBEPP_CPP17_INLINE_VAR=inline \
"SBEPP_CPP17_NODISCARD=[[nodiscard]]"
"SBEPP_CPP17_NODISCARD=[[nodiscard]]"\
SBEPP_DEPRECATED

# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
Expand Down
267 changes: 267 additions & 0 deletions doc/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,271 @@ void make_msg(
auto m = sbepp::make_view<market::messages::msg>(buf.data(), buf.size());
// encode message as usual...
}
```

---

## Stringification {#stringification-example}

Here's an example of how to build stringification visitor using
[fmt](https://github.com/fmtlib/fmt) and [Visit API](#visit-api) (you can find
the full example in `stringification.test.cpp`):

```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());
sbepp::visit(sbepp::get_header(m), *this);
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)
{
on_array(d, sbepp::data_traits<Tag>::name());
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)
{
on_encoding(c, sbepp::composite_traits<Tag>::name());
return {};
}

template<typename Tag>
void on_enum_value(auto /*e*/, Tag)
{
append("{}\n", sbepp::enum_value_traits<Tag>::name());
}

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;
append("{}", sbepp::set_choice_traits<Tag>::name());
}
}

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++;
sbepp::visit_children(c, *this);
indentation--;
}
};

// usage:
auto res = sbepp::visit<to_string_visitor>(message);
fmt::print("{}", res.str());
```

Now, for a message like:

```xml
<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
```
Loading
Loading