Value interface
Introduction
bvalue
is class that represents a bencoded value.
bvalue
can be used to create and edit bencode values.
It is a sum-type implemented with a std::variant
.
The possible alternative types stored in a bvalue
are the bencode data types:
integer, string, list, dict, and a special uninitialized type.
bvalue
is an instantiation of the class template
template <typename Policy> basic_bvalue
and the exact types used to store
the different bencode data types can be customized through the Policy template argument.
The default policy uses following storage types for the possible alternatives:
integer |
|
string |
|
list |
|
dict |
|
Note
In most examples bvalue
can be replaced by
basic_bvalue
with a user-defined Policy template argument.
Construction
bvalue
provides similar constructors and assignment operators as std::variant
.
Construct an empty bvalue.
auto b = bc::bvalue {};
Arguments matching one of the storage types will be forwarded to the constructor of the underlying type.
// Copy initialize an integer
bc::bvalue bi = 3;
// Copy initialize a bvalue holding a string.
bc::bvalue b3 = "string";
The type can be passed explicitly using one of the following type tags:
bc::btype::integer
bc::btype::string
bc::btype::list
bc::btype::dict
This is necessary to distinguish between initializer-lists for string, list and dict bencode types. The constructor taking an initializer-list without a type tag will construct a dict.
// initialized-list without type-tag constructs a dict.
auto dict_from_init_list = bc::bvalue({{"foo", 1}});
// construct a vector of 5 times 1
bc::bvalue b4(bc::btype::list, {5, 1});
The converting constructor allow any type that has the required customization points implemented to be passed to the bvalue constructor.
#include <bencode/traits/list.hpp>
auto l = std::list{1, 2, 4, 5};
auto b = bencode::bvalue(l);
Type checking
Checking the alternative of a bvalue
can be done using the following functions:
template <typename Policy> bool holds_integer(const basic_bvalue<Policy>&)
template <typename Policy> bool holds_string(const basic_bvalue<Policy>&)
template <typename Policy> bool holds_list(const basic_bvalue<Policy>&)
template <typename Policy> bool holds_dict(const basic_bvalue<Policy>&)
template <enum bencode_type E, typename Policy> bool holds_alternative(const basic_bvalue<Policy>&)
template <bview_alternative_type T, typename Policy> bool holds_alternative(const basic_bvalue<Policy>&)
The holds_<type>
(where <type>
is one of the bencode data types)
functions are convenience functions that wrap the templated holds_alternative
function.
auto b = bencode::bvalue({{"a", 1}, {"b", 2}});
holds_integer(b); // returns false
holds_dict(b); // returns true
// type tag based check
bc::holds_alternative<bc::type::dict>(b); // returns true
// bvalue access with the exact storage type.
using T = std::map<std::string, bv::bvalue>;
bc::holds_alternative<T>(b); // returns true
Accessors
Accessor functions are used to get access to the alternative types stored in a bvalue
.
Throwing accessor function will throw bad_bvalue_access
when the current
activate alternative type does not match the access type.
Non throwing accessor functions will return a pointer to the alternative type or a nullptr.
The interface is similar to that of std::variant
.
Except that there are aliases (eg. get_integer
, get_if_integer
) for all possible
alternative types.
Accessor functions documentation can be found here:
bc::bvalue b = "string";
using bv = bc::bencode_type;
// access by enum / type tag
auto& s1 = get<bc::btype::string>(b); // is equivalent to: get<bc::bencode_type::string>(b);
// access by exact alterantive type
auto& s1 = get<std::string>(b);
// or the more succinct version
auto& s = get_string(b);
auto& l = get_list(b); // throws bad_bvalue_access
// move the string out of the bvalue.
auto s = get_string(std::move(b));
Non throwing accessors.
bc::bvalue b = "string";
using bv = bc::bencode_type;
// access by enum / type tag
auto* s1 = get_if<bc::btype::string>(&b);
// access by exact type
auto* s2 = get_if<std::string>(&b);
// or the more succinct version
auto* s = get_if_string(&b);
auto* l = get_if_list(b); // l is nullptr
Conversion
Retrieving the value contained in a bvalue
as another type can be done using the
converting accessor functions.
get_as<T>(const bvalue&)
is a throwing converter which will throw
bad_conversion
when the current active alternative can not be converted to the
requested type.
try_get_as<T>(const bvalue&)
is a non throwing converter an will return the result as a
nonstd::expected
type.
bencode::bvalue b = "string";
auto bytes = get_as<std::vector<std::byte>>(b);
auto bytes = try_get_as<std::vector<std::byte>>(b);
if (bytes.has_value()) {
// do something with *bytes
} else {
// do something with the error code: bytes.error()
}
Modifying operations
The emplace family of functions will discard the current value and construct a new value in place forwarding the arguments to the constructor of the underlying type.
auto b = bc::bvalue(20);
b.emplace_list({1, 2, 3});
auto& l = get_list(b);
There are some convenience functions when the underlying type is a list or dict to directly access the underlying type without first obtaining a reference with accessor functions.
auto blist = bc::bvalue(bc::type::list, {1, 2, 3, 4});
auto bdict = bc::bdict(bc::type::dict, {
{"one", 1}.
{"two", 2}
});
// get reference to the first element of an array
auto& first = blist.at(0);
// change second item
blist[1] = 0;
// get reference to the value for key "one"
auto& first_val = bdict.at("one");
first_val["one"] = 2;
// append new elements
blist.push_back(5);
blist.emplace_back(bc::type::list, {"a", "b", "c"});
// check if there is a key "one"
auto has_one = bdict.contains("one");
// clear elements
blist.clear();
bdict.clear();
Comparison
Comparison operators will look through the bvalue and compare with the underling type.
If the type you compare with does not match the type of the value contained in bvalue
the fallback ordering is defined by the ordering of the types:
integer < string < list < dict
.
bc::bvalue b_int(2);
// true
auto t = (b == 2);
bc::bvalue b_string("test");
// true
auto t2 = b_string < "zzzz"
bc::bvalue b_dict(bc::btype::dict, {{"one", 1}, {"two", 2}});
auto dict = std::map({{"one", 1}, {"two", 2}});
// returns true
b_dict == dict;
Policies
The types used to store the different alternatives can be modified with the Policy template argument.