User-defined type support
There are various customization points to integrate user-defined types in this library.
Serialization traits
The first step to integrating a custom type is by providing which bencode data type it encodes
to by specializing template <typename T> serialization_traits
for your type.
Serialization traits has a single member type
of type bencode_type
that defines what bencode data type this type serializes to.
To make specialization of serialization_traits
easy a few helpers are provided.
- Helper classes:
serializes_to_runtime_type
serializes_to_integer
serializes_to_integer
serializes_to_string
serializes_to_list
template <dict_key_order Order = dict_key_order::sorted> struct serializes_to_dict
- Helper macros:
BENCODE_SERIALIZES_TO_RUNTIME_TYPE
BENCODE_SERIALIZES_TO_INTEGER
BENCODE_SERIALIZES_TO_STRING
BENCODE_SERIALIZES_TO_LIST
BENCODE_SERIALIZES_TO_DICT_SORTED
BENCODE_SERIALIZES_TO_DICT_UNSORTED
When the user-defined type can be converted to different bencode data types depending on the value
serializes_to_runtime_type
or BENCODE_SERIALIZES_TO_RUNTIME_TYPE
should be used.
When a type serializes to a dict we make a differentiation between sorted and unsorted dicts. Since a bencode dict requires keys to be in sorted order we must mark map-like types with unsorted keys as such.
To define a type that behaves like a pointer (eg. smart pointers), the static member variable
is_pointer
must be set to true.
This will make sure that when serializing/deserializing that type the value is dereferenced when needed.
For types that behave like standard library types, specializing
template <typename T> serialization_traits
can be enough to enable full support.
The bencode library will try to find an implementation that works for given type.
If no suitable build-in methods exist, additional customization points must be implemented.
After specializing serialization_traits
the user-defined type satisfies
the serializable
concept.
Example:
struct rgb_color
{
std::uint8_t r, g, g;
};
// Specialization with a macro.
namespace bencode {
BENCODE_SERIALIZES_TO_LIST(rgb_color)
}
// Equivalent specialization without macro use.
namespace bencode {
template <> struct serialization_traits<rgb_color> : serializes_to_list {};
}
template <typename T>
class my_smart_pointer : {...}
namespace bencode {
template <typename T>
struct serialization_traits<my_smart_pointer<T>>
: serializes_to<serialization_traits<T>::type>
{
static constexpr bool is_pointer = true;
};
}
Event producer
The second required customization point to enable support for a user-defined type is
the bencode_connect()
.
template <event_consumer EC>
constexpr void bencode_connect(
customization_point_type<rgb_color>, EC& consumer, const rgb_color& value)
{
consumer.list_begin();
consumer.integer(value.r);
consumer.list_item();
consumer.integer(value.g);
consumer.list_item();
consumer.integer(value.b);
consumer.list_item();
consumer.list_end()
}
After overriding this function the type satisfies the event_producer
concept.
After satisfying serializable
and cpp:concept:event_producer the user defined type
can be serialized with encoder
and assigned to bvalue
.
Important
All customization points prefixed with bencode_
must be defined in the
namespace of the type for which you want to enable a library feature.
These functions use Argument-dependent lookup (ADL) to identify the correct overload.
Assignment to bvalue
Types that satisfy event_producer
have a default implementation
that allows the type to be assigned to bvalue
, but is not always the most efficient.
The default can be overriden by overriding bencode_assign_to_bvalue()
template <typename Policy>
constexpr auto bencode_assign_to_bvalue(
customization_point_type<rgb_color>, basic_bvalue<Policy>& bv, const rgb_color& value)
{
auto& l = bv.emplace_list();
l.push_back(value.r);
l.push_back(value.g);
l.push_back(value.b);
}
Direct comparison to bvalue
The content of a bvalue
can be compared with that of a custom type without
creating a temporary bvalue
object.
This is done be overriding bencode_compare_equality_with_bvalue()
template <typename Policy>
bencode_compare_equality_with_bvalue(
customization_point_type<rgb_color>, basic_bvalue<Policy>& bv, const rgb_color& value)
{
if (!is_list(bv)) return false;
if (bv.size() != 3) return false;
return (bv[0] == value.r) && (bv[1] == value.g) && (b[2] == value.b);
}
For types that can be ordered bencode_compare_three_way_with_bvalue()
can be overridden.
template <typename Policy>
std::partial_ordering bencode_compare_three_way_with_bvalue(
customization_point_type<rgb_color>, basic_bvalue<Policy>& bv, const rgb_color& value)
{
if (!is_list(bv)) return std::partial_ordering::unordered;
if (bv.size() < 3) return std::partial_ordering::greater;
if (bv.size() > 3) return std::partial_ordering::less;
auto first_ordering = (bv[0] <=> value.r);
if (first_ordering == std::partial_ordering::equivalent) {
auto second_ordering = (bv[1] <=> value.g);
if (second_ordering == std::partial_ordering::equivalent) {
return b[2] <=> value.b;
} else {
return second_ordering;
}
}
return first_ordering
}
Conversion from bvalue to custom type
You can retrieve your custom type directly from a :cpp:clas::bvalue
by implementing the bencode_convert_from_bvalue()
customization point.
This will allow the use of get_as
with your type.
Errors are reported with nonstd::expected
.
template <typename Policy>
nonstd::expected<rgb_color, conversion_errc>
bencode_convert_from_bvalue(customization_point_type<rgb_color>, const basic_bvalue<Policy>& bv)
{
if (!is_list(bv))
return nonstd::make_unexpected(conversion_errc::not_list_type);
const auto& l = get_list(bv)
if (l.size() != 3)
return nonstd::make_unexpected(conversion_errc::size_mismatch);
return rgb_color {.r = l[0], .g = l[1], .b = l[2]};
}
Direct comparison to bview
Analogue with comparison with bvalue
there are two comparison customization points
for bview
:
bencode_compare_equality_with_bview()
bencode_compare_three_way_with_bview()
The implementation for our example user-defined class is exactly the same as for
the implementation for bvalue
, except the function signature.
constexpr bool bencode_compare_equality_with_bview(
customization_point_type<rgb_color>, const bview& bv, rgb_color value);
constexpr bool bencode_compare_three_way_with_bview(
customization_point_type<rgb_color>, const bview& bv, rgb_color value);
Conversion from bview to custom type
Similar to conversion from bvalue
there is a conversion from bview
by implementing the bencode_convert_from_bview()
customization point.
The implementation for our example user-defined class is exactly the same as for
the implementation for bvalue
, except the function signature.
nonstd::expected<rgb_color, conversion_errc>
bencode_convert_from_bview(customization_for<rgb_color>, const bview& bv);