From 59a35d82d7ae5d787e663a108cfd709e0b7640c2 Mon Sep 17 00:00:00 2001 From: Evelyn LePain Date: Fri, 29 Nov 2024 18:56:25 -0700 Subject: [PATCH] deserialization --- include/redis-cpp/resp/deserialization.h | 754 ++++++++++++++++++++-- include/redis-cpp/resp/detail/marker.h | 11 + include/redis-cpp/resp/detail/value_ptr.h | 66 ++ include/redis-cpp/value.h | 220 ++++++- 4 files changed, 982 insertions(+), 69 deletions(-) create mode 100644 include/redis-cpp/resp/detail/value_ptr.h diff --git a/include/redis-cpp/resp/deserialization.h b/include/redis-cpp/resp/deserialization.h index 4a620b3..2b7e43c 100644 --- a/include/redis-cpp/resp/deserialization.h +++ b/include/redis-cpp/resp/deserialization.h @@ -18,10 +18,17 @@ #include #include #include +#include +#include +#include + +// BOOST +#include // REDIS-CPP #include #include +#include namespace rediscpp { @@ -30,20 +37,28 @@ inline namespace resp namespace deserialization { [[nodiscard]] -inline auto get_mark(std::istream &stream) +inline char get_mark(std::istream &stream) { - switch (stream.get()) + auto mark = stream.get(); + switch (mark) { case detail::marker::simple_string : - return detail::marker::simple_string; case detail::marker::error_message : - return detail::marker::error_message; case detail::marker::integer : - return detail::marker::integer; case detail::marker::bulk_string : - return detail::marker::bulk_string; case detail::marker::array : - return detail::marker::array; + case detail::marker::null : + case detail::marker::boolean : + case detail::marker::double_float : + case detail::marker::big_number : + case detail::marker::bulk_error : + case detail::marker::verbatim_string : + case detail::marker::map : + case detail::marker::attributes_alt : + case detail::marker::attributes : + case detail::marker::set : + case detail::marker::push : + return char(mark); default: break; } @@ -60,9 +75,109 @@ T get(std::istream &stream) return {stream}; } -class simple_string final + +class mapped_data final { public: + struct items_type; + + mapped_data(std::istream &stream); + mapped_data(const mapped_data &other) noexcept; + mapped_data(mapped_data &&other) noexcept; + mapped_data& operator=(const mapped_data &other) noexcept; + mapped_data& operator=(mapped_data &&other) noexcept; + ~mapped_data() noexcept; + + [[nodiscard]] + std::size_t size() const noexcept; + + [[nodiscard]] + items_type const& get() const noexcept; + +private: + [[nodiscard]] + items_type* get_map() noexcept + { + return reinterpret_cast(map_buffer); + } + [[nodiscard]] + items_type const* get_map() const noexcept + { + return reinterpret_cast(map_buffer); + } + + alignas(std::map>) std::byte map_buffer[sizeof(std::map>)]; +}; + +class attributes final +{ +public: + static constexpr auto marker = detail::marker::attributes; + using items_type = mapped_data::items_type; + + attributes(std::istream &stream) + : items_(stream) + {} + + [[nodiscard]] + std::size_t size() const noexcept { return items_.size(); } + + [[nodiscard]] + items_type const& get() const noexcept { return items_.get(); } +private: + mapped_data items_; +}; + +template +auto operator<(const T& rhs, const T& lhs) -> decltype(rhs.get() < lhs.get()) +{ + return rhs.get() < lhs.get(); +} + +struct set_attributes_fn +{ + template + void operator()(T& value, detail::value_ptr attrs) const + { + value.attributes_ = std::move(attrs); + } + + template + void operator()(std::variant& variant, detail::value_ptr attrs) const + { + std::visit([&](auto& value){(*this)(value, std::move(attrs));}, variant); + } +}; + +constexpr set_attributes_fn set_attributes{}; + +class attributed +{ +public: + bool has_attributes() const + { + return bool(attributes_); + } + + attributes& get_attributes() const + { + if(has_attributes()) + { + return *attributes_; + } + throw std::bad_optional_access(); + } + + friend struct set_attributes_fn; + +protected: + detail::value_ptr attributes_; +}; + +class simple_string final: private attributed +{ +public: + static constexpr auto marker = detail::marker::simple_string; simple_string(std::istream &stream) { std::getline(stream, value_); @@ -75,13 +190,18 @@ class simple_string final return value_; } + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + private: std::string value_; }; -class error_message final +class error_message final: private attributed { public: + static constexpr auto marker = detail::marker::error_message; error_message(std::istream &stream) { std::getline(stream, value_); @@ -94,13 +214,18 @@ class error_message final return value_; } + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + private: std::string value_; }; -class integer final +class integer final: private attributed { public: + static constexpr auto marker = detail::marker::integer; integer(std::istream &stream) { std::string string; @@ -114,6 +239,10 @@ class integer final return value_; } + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + private: std::int64_t value_; }; @@ -169,9 +298,10 @@ class binary_data final buffer_type data_; }; -class bulk_string final +class bulk_string final: private attributed { public: + static constexpr auto marker = detail::marker::bulk_string; bulk_string(std::istream &stream) : data_{stream} { @@ -189,72 +319,325 @@ class bulk_string final return data_.get(); } + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + private: binary_data data_; }; -class null final +class null final: private attributed { public: + static constexpr auto marker = detail::marker::null; + null(std::istream &stream) + { + std::string string; + std::getline(stream, string); + } + void get() const noexcept { } + + static constexpr bool is_null() { return true; } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; +}; + +bool operator<(const null&, const null&) +{ + return false; +} + +class boolean final: private attributed +{ +public: + static constexpr auto marker = detail::marker::boolean; + boolean(std::istream &stream) + { + std::string string; + std::getline(stream, string); + switch (string.front()) + { + case 't': + value_ = true; + break; + case 'f': + value_ = false; + break; + default: + throw std::invalid_argument{ + "[rediscpp::resp::deserialization::boolean] " + "Bad input format. Unsupported boolian value." + }; + } + } + + [[nodiscard]] + bool get() const noexcept + { + return value_; + } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + bool value_; +}; + +class double_float final: private attributed +{ +public: + static constexpr auto marker = detail::marker::double_float; + double_float(std::istream &stream) + { + std::string string; + std::getline(stream, string); + value_ = std::stod(string); + } + + [[nodiscard]] + bool get() const noexcept + { + return value_; + } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + double value_; +}; + +class big_number final: private attributed +{ +public: + static constexpr auto marker = detail::marker::big_number; + big_number(std::istream &stream) + { + std::getline(stream, value_); + value_.pop_back(); // removing '\r' from string + } + + [[nodiscard]] + std::string_view get() const noexcept + { + return value_; + } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + std::string value_; +}; + +class bulk_error final: private attributed +{ +public: + static constexpr auto marker = detail::marker::bulk_error; + bulk_error(std::istream &stream) + : data_{stream} + { + } + + [[nodiscard]] + std::string_view get() const noexcept + { + return data_.get(); + } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + binary_data data_; +}; + +class verbatim_string final: private attributed +{ +public: + static constexpr auto marker = detail::marker::verbatim_string; + constexpr static size_t encoding_length = 3; + + verbatim_string(std::istream &stream) + { + std::string string; + std::getline(stream, string); + auto const length = std::stoll(string); + data_.resize(static_cast(length) + encoding_length); + stream.read(&data_[0], encoding_length); + stream.get(); + stream.read(&data_[encoding_length], length); + std::getline(stream, string); + } + + [[nodiscard]] + std::string_view get() const noexcept + { + return {data(), size()}; + } + + [[nodiscard]] + std::string_view get_encoding() const noexcept + { + return {std::data(data_), encoding_length}; + } + + [[nodiscard]] + std::size_t size() const noexcept + { + return std::size(data_) - encoding_length; + } + + [[nodiscard]] + char const* data() const noexcept + { + return std::data(data_) + encoding_length; + } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + using buffer_type = std::vector; + buffer_type data_; +}; + +class map final: private attributed +{ +public: + static constexpr auto marker = detail::marker::map; + using items_type = mapped_data::items_type; + + map(std::istream &stream) + : items_(stream) + {} + + [[nodiscard]] + std::size_t size() const noexcept { return items_.size(); } + + [[nodiscard]] + items_type const& get() const noexcept { return items_.get(); } + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + mapped_data items_; +}; + +class set final: private attributed +{ +public: + static constexpr auto marker = detail::marker::set; + struct items_type; + + set(std::istream &stream); + set(const set &other) noexcept; + set(set &&other) noexcept; + set& operator=(const set &other) noexcept; + set& operator=(set &&other) noexcept; + ~set() noexcept; + + [[nodiscard]] + std::size_t size() const noexcept; + + [[nodiscard]] + items_type const& get() const noexcept; + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + [[nodiscard]] + items_type* get_set() noexcept + { + return reinterpret_cast(set_buffer); + } + [[nodiscard]] + items_type const* get_set() const noexcept + { + return reinterpret_cast(set_buffer); + } + + alignas(std::set>) std::byte set_buffer[sizeof(std::set>)]; +}; + +class push final: private attributed +{ +public: + static constexpr auto marker = detail::marker::push; + struct items_type; + + push(std::istream &stream); + push(const push &other) noexcept; + push(push &&other) noexcept; + push& operator=(const push &other) noexcept; + push& operator=(push &&other) noexcept; + ~push() noexcept; + + [[nodiscard]] + std::size_t size() const noexcept; + + [[nodiscard]] + items_type const& get() const noexcept; + + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + +private: + [[nodiscard]] + items_type* get_items() noexcept + { + return reinterpret_cast(vector_buffer); + } + [[nodiscard]] + items_type const* get_items() const noexcept + { + return reinterpret_cast(vector_buffer); + } + + alignas(std::vector) std::byte vector_buffer[sizeof(std::vector)]; }; -class array final +class array final: private attributed { public: + static constexpr auto marker = detail::marker::array; using item_type = std::variant< simple_string, error_message, integer, bulk_string, array, - null + null, + boolean, + double_float, + big_number, + bulk_error, + verbatim_string, + map, + set, + push >; using items_type = std::vector; - array(std::istream &stream) - { - std::string string; - std::getline(stream, string); - auto count = std::stoll(string); - if (count < 0) - { - is_null_ = true; - return; - } - if (count < 1) - return; - items_.reserve(static_cast(count)); - while (count--) - { - switch (get_mark(stream)) - { - case detail::marker::simple_string : - items_.emplace_back(simple_string{stream}); - break; - case detail::marker::error_message : - items_.emplace_back(error_message{stream}); - break; - case detail::marker::integer : - items_.emplace_back(integer{stream}); - break; - case detail::marker::bulk_string : - items_.emplace_back(bulk_string{stream}); - break; - case detail::marker::array : - items_.emplace_back(array{stream}); - break; - default: - throw std::invalid_argument{ - "[rediscpp::resp::deserialization::array] " - "Bad input format. Unsupported value type." - }; - } - } - } + array(std::istream &stream); [[nodiscard]] bool is_null() const noexcept @@ -274,11 +657,274 @@ class array final return items_; } + using attributed::has_attributes; + using attributed::get_attributes; + friend struct set_attributes_fn; + private: bool is_null_ = false; items_type items_; }; +constexpr char get_marker(size_t index) +{ + using namespace boost::mp11; + return mp_with_index>(index, [](auto idx) + { + using idx_t = decltype(idx); + return std::variant_alternative_t::marker; + }); +} + +constexpr char get_marker(const array::item_type& item) +{ + return get_marker(item.index()); +} + +array::item_type parse_item(std::istream& stream) +{ + switch (get_mark(stream)) + { + case detail::marker::simple_string : + return simple_string{stream}; + case detail::marker::error_message : + return error_message{stream}; + case detail::marker::integer : + return integer{stream}; + case detail::marker::bulk_string : + return bulk_string{stream}; + case detail::marker::array : + return array{stream}; + case detail::marker::null : + return null{stream}; + case detail::marker::boolean : + return boolean{stream}; + case detail::marker::double_float : + return double_float{stream}; + case detail::marker::big_number : + return big_number{stream}; + case detail::marker::bulk_error : + return bulk_error{stream}; + case detail::marker::verbatim_string : + return verbatim_string{stream}; + case detail::marker::map : + return map{stream}; + case detail::marker::attributes_alt : + case detail::marker::attributes : + { + auto attrs = detail::make_value_ptr(stream); + auto value = parse_item(stream); + set_attributes(value, std::move(attrs)); + return std::move(value); + } + case detail::marker::set : + return set{stream}; + case detail::marker::push : + return push{stream}; + default: + break; + } + + throw std::invalid_argument{ + "[rediscpp::resp::deserialization::parse_item] " + "Bad input format. Unsupported value type." + }; +} + +array::array(std::istream &stream) +{ + std::string string; + std::getline(stream, string); + auto count = std::stoll(string); + if (count < 0) + { + is_null_ = true; + return; + } + if (count < 1) + return; + items_.reserve(static_cast(count)); + while (count--) + { + items_.emplace_back(deserialization::parse_item(stream)); + } +} + +struct mapped_data::items_type: std::map> +{}; + +mapped_data::mapped_data(std::istream &stream) +{ + new(get_map()) items_type{}; + auto& items_ = *get_map(); + + std::string string; + std::getline(stream, string); + auto count = std::stoll(string); + + while (count--) + { + auto key = parse_item(stream); + items_.emplace(std::move(key), parse_item(stream)); + } +} + +mapped_data::mapped_data(const mapped_data &other) noexcept +{ + new(get_map()) items_type{*other.get_map()}; +} + +mapped_data::mapped_data(mapped_data &&other) noexcept +{ + new(get_map()) items_type{std::move(*other.get_map())}; +} + +mapped_data& mapped_data::operator=(const mapped_data &other) noexcept +{ + *get_map() = *other.get_map(); + return *this; +} + +mapped_data& mapped_data::operator=(mapped_data &&other) noexcept +{ + *get_map() = std::move(*other.get_map()); + return *this; +} + +mapped_data::~mapped_data() noexcept +{ + get_map()->~map(); +} + +[[nodiscard]] +std::size_t mapped_data::size() const noexcept +{ + return std::size(get()); +} + +[[nodiscard]] +auto mapped_data::get() const noexcept -> items_type const& +{ + return *get_map(); +} + +struct set::items_type: std::set> +{}; + +set::set(std::istream &stream) +{ + new(get_set()) items_type{}; + auto& items_ = *get_set(); + + std::string string; + std::getline(stream, string); + auto count = std::stoll(string); + + while (count--) + { + items_.emplace(parse_item(stream)); + } +} + +set::set(const set &other) noexcept +{ + new(get_set()) items_type{*other.get_set()}; +} + +set::set(set &&other) noexcept +{ + new(get_set()) items_type{std::move(*other.get_set())}; +} + +set& set::operator=(const set &other) noexcept +{ + *get_set() = *other.get_set(); + return *this; +} + +set& set::operator=(set &&other) noexcept +{ + *get_set() = std::move(*other.get_set()); + return *this; +} + +set::~set() noexcept +{ + get_set()->~set(); +} + +[[nodiscard]] +std::size_t set::size() const noexcept +{ + return std::size(get()); +} + +[[nodiscard]] +auto set::get() const noexcept -> items_type const& +{ + return *get_set(); +} + +struct push::items_type: std::vector +{}; + +push::push(std::istream &stream) +{ + new(get_items()) items_type{}; + + auto& items_ = *get_items(); + + std::string string; + std::getline(stream, string); + auto count = std::stoll(string); + if (count < 1) + return; + items_.reserve(static_cast(count)); + while (count--) + { + items_.emplace_back(parse_item(stream)); + } +} + +push::push(const push &other) noexcept +{ + new(get_items()) items_type{*other.get_items()}; +} + +push::push(push &&other) noexcept +{ + new(get_items()) items_type{std::move(*other.get_items())}; +} + +push& push::operator=(const push &other) noexcept +{ + *get_items() = *other.get_items(); + return *this; +} + +push& push::operator=(push &&other) noexcept +{ + *get_items() = std::move(*other.get_items()); + return *this; +} + +push::~push() noexcept +{ + get_items()->~vector(); +} + +[[nodiscard]] +std::size_t push::size() const noexcept +{ + return std::size(get()); +} + +[[nodiscard]] +auto push::get() const noexcept -> items_type const& +{ + return *get_items(); +} + } // namespace deserialization } // namespace resp } // namespace rediscpp diff --git a/include/redis-cpp/resp/detail/marker.h b/include/redis-cpp/resp/detail/marker.h index 7893dd5..b27416c 100644 --- a/include/redis-cpp/resp/detail/marker.h +++ b/include/redis-cpp/resp/detail/marker.h @@ -25,6 +25,17 @@ constexpr auto error_message = '-'; constexpr auto integer = ':'; constexpr auto bulk_string = '$'; constexpr auto array = '*'; +constexpr auto null = '_'; +constexpr auto boolean = '#'; +constexpr auto double_float = ','; +constexpr auto big_number = '('; +constexpr auto bulk_error = '!'; +constexpr auto verbatim_string = '='; +constexpr auto map = '%'; +constexpr auto attributes = '|'; +constexpr auto attributes_alt = '`'; +constexpr auto set = '~'; +constexpr auto push = '>'; constexpr auto cr = '\r'; constexpr auto lf = '\n'; diff --git a/include/redis-cpp/resp/detail/value_ptr.h b/include/redis-cpp/resp/detail/value_ptr.h new file mode 100644 index 0000000..e4a3c0b --- /dev/null +++ b/include/redis-cpp/resp/detail/value_ptr.h @@ -0,0 +1,66 @@ +#ifndef REDISCPP_RESP_DETAIL_VALUE_PTR_H_ +#define REDISCPP_RESP_DETAIL_VALUE_PTR_H_ + +#include + +namespace rediscpp +{ +inline namespace resp +{ +namespace detail +{ +template, class = std::enable_if_t && !std::is_polymorphic_v>> +class value_ptr: public std::unique_ptr +{ + using parent_t = std::unique_ptr; +public: + template>> + value_ptr(Args&&... args) + : parent_t(std::forward(args)...) + {} + + value_ptr(const value_ptr& other) + : parent_t(other ? std::make_unique(*other) : nullptr) + {} + value_ptr& operator=(const value_ptr& other) + { + if(other) + { + if(!*this) + { + parent_t::operator=(std::make_unique(*other)); + } + else + { + **this = *other; + } + } + else + { + parent_t::reset(); + } + return *this; + } + value_ptr& operator=(value_ptr&& other) + { + parent_t::operator=(std::move(other)); + return *this; + } + value_ptr& operator=(std::nullptr_t) + { + parent_t::operator=(nullptr); + return *this; + } +}; + +template +value_ptr make_value_ptr(Args&& ...args) +{ + return std::make_unique(std::forward(args)...); +} + +} // namespace detail +} // namespace resp +} // namespace rediscpp + +#endif // !REDISCPP_RESP_DETAIL_VALUE_PTR_H_ diff --git a/include/redis-cpp/value.h b/include/redis-cpp/value.h index 6226d7f..97da3cc 100644 --- a/include/redis-cpp/value.h +++ b/include/redis-cpp/value.h @@ -51,17 +51,58 @@ class value final case resp::detail::marker::array : item_ = std::make_unique(resp::deserialization::array{stream}); break; + case resp::detail::marker::null : + item_ = std::make_unique(resp::deserialization::null{stream}); + break; + case resp::detail::marker::boolean : + item_ = std::make_unique(resp::deserialization::boolean{stream}); + break; + case resp::detail::marker::double_float : + item_ = std::make_unique(resp::deserialization::double_float{stream}); + break; + case resp::detail::marker::big_number : + item_ = std::make_unique(resp::deserialization::big_number{stream}); + break; + case resp::detail::marker::bulk_error : + item_ = std::make_unique(resp::deserialization::bulk_error{stream}); + break; + case resp::detail::marker::verbatim_string : + item_ = std::make_unique(resp::deserialization::verbatim_string{stream}); + break; + case resp::detail::marker::map : + item_ = std::make_unique(resp::deserialization::map{stream}); + break; + case resp::detail::marker::attributes_alt : + case resp::detail::marker::attributes : + { + auto attrs = resp::detail::make_value_ptr(stream); + item_ = std::make_unique(resp::deserialization::parse_item(stream)); + resp::deserialization::set_attributes(*item_, std::move(attrs)); + break; + } + case resp::detail::marker::set : + item_ = std::make_unique(resp::deserialization::set{stream}); + break; + case resp::detail::marker::push : + item_ = std::make_unique(resp::deserialization::push{stream}); + break; default : break; } } value(item_type const &item) - : marker_{resp::detail::marker::array} + : marker_{get_marker(item)} , item_{std::make_unique(item)} { } + value(item_type &&item) + : marker_{get_marker(item)} + , item_{std::make_unique(std::move(item))} + { + } + [[nodiscard]] bool empty() const noexcept { @@ -107,10 +148,71 @@ class value final return marker_ == resp::detail::marker::array; } + [[nodiscard]] + bool is_null() const noexcept + { + return marker_ == resp::detail::marker::null + || (item_ ? std::visit([](auto val){return is_null(&val);}, *item_) : false); + } + + [[nodiscard]] + bool is_boolean() const noexcept + { + return marker_ == resp::detail::marker::boolean; + } + + [[nodiscard]] + bool is_double() const noexcept + { + return marker_ == resp::detail::marker::double_float; + } + + [[nodiscard]] + bool is_big_number() const noexcept + { + return marker_ == resp::detail::marker::big_number; + } + + [[nodiscard]] + bool is_bulk_error() const noexcept + { + return marker_ == resp::detail::marker::bulk_error; + } + + [[nodiscard]] + bool is_verbatim_string() const noexcept + { + return marker_ == resp::detail::marker::verbatim_string; + } + + [[nodiscard]] + bool is_map() const noexcept + { + return marker_ == resp::detail::marker::map; + } + + [[nodiscard]] + bool is_set() const noexcept + { + return marker_ == resp::detail::marker::set; + } + + [[nodiscard]] + bool is_push() const noexcept + { + return marker_ == resp::detail::marker::push; + } + [[nodiscard]] bool is_string() const noexcept { - return is_simple_string() || is_bulk_string(); + return is_simple_string() || is_bulk_string() || is_verbatim_string(); + } + + [[nodiscard]] + bool is_error() const noexcept + { + return is_error_message() || is_bulk_error(); } [[nodiscard]] @@ -137,12 +239,84 @@ class value final return get_value(); } + [[nodiscard]] + auto as_boolean() const + { + return get_value(); + } + + [[nodiscard]] + auto as_double() const + { + return get_value(); + } + + [[nodiscard]] + auto as_big_number() const + { + return get_value(); + } + + [[nodiscard]] + auto as_bulk_error() const + { + return get_value(); + } + + [[nodiscard]] + auto as_verbatim_string() const + { + return get_value(); + } + + [[nodiscard]] + auto& as_map() const + { + return get_value>&, resp::deserialization::map>(); + } + + [[nodiscard]] + auto& as_set() const + { + return get_value>&, resp::deserialization::set>(); + } + + [[nodiscard]] + auto& as_push() const + { + return get_value&, resp::deserialization::push>(); + } + + [[nodiscard]] + auto& as_array() const + { + return get_value&, resp::deserialization::array>(); + } + [[nodiscard]] auto as_string() const { return is_simple_string() ? - get_value() : - get_value(); + as_simple_string() : + is_bulk_string() ? + as_bulk_string() : + as_verbatim_string(); + } + + [[nodiscard]] + auto as_error() const + { + return is_error_message() ? + as_error_message() : + as_bulk_error(); + } + + [[nodiscard]] + auto& as_vector() const + { + return is_array() ? + as_array() : + as_push(); } [[nodiscard]] @@ -169,11 +343,25 @@ class value final { if (empty()) throw std::runtime_error{"Empty value."}; - if (is_error_message()) - throw std::runtime_error{std::string{as_error_message()}}; + if (is_error()) + throw std::runtime_error{std::string{as_error()}}; return T{get_value>()}; } + bool has_attributes() + { + if (empty()) + throw std::runtime_error{"Empty value."}; + return std::visit([](auto& value){return value.has_attributes();}, *item_); + } + + const deserialization::attributes& get_attributes() + { + if (empty()) + throw std::runtime_error{"Empty value."}; + return std::visit([](auto& value) -> decltype(auto) {return value.get_attributes();}, *item_); + } + private: char marker_; std::unique_ptr item_; @@ -209,30 +397,32 @@ class value final template R get_value() const { - R result; - std::visit(resp::detail::overloaded{ - [] (auto const &) + return std::visit(resp::detail::overloaded{ + [] (auto const &) -> R { throw std::bad_cast{}; }, - [&result] (T const &val) + [] (T const &val) -> R { if (is_null(&val)) throw std::logic_error("You can't cast Null to a type."); - result = val.get(); + return val.get(); } }, get()); - - return result; } + template + static constexpr auto holds_vector = boost::mp11::mp_similar().get())>, std::vector>::value; + template std::vector get_array() const { std::vector result; std::visit(resp::detail::overloaded{ - [] (auto const &) + [] ([[maybe_unused]]auto const &val) + -> std::enable_if_t> { throw std::bad_cast{}; }, - [&result] (resp::deserialization::array const &val) + [&result] (auto const &val) + -> std::enable_if_t> { if (is_null(&val)) throw std::logic_error("You can't cast Null to a type.");