#ifndef RFL_PARSING_VIEWREADER_HPP_ #define RFL_PARSING_VIEWREADER_HPP_ #include #include #include #include #include "../Result.hpp" #include "../internal/is_array.hpp" namespace rfl::parsing { template class ViewReader { private: using InputVarType = typename R::InputVarType; static constexpr size_t size_ = ViewType::size(); public: ViewReader(const R* _r, ViewType* _view, std::array* _found, std::array* _set, std::vector* _errors) : r_(_r), view_(_view), found_(_found), set_(_set), errors_(_errors) {} ~ViewReader() = default; template void read(const std::string_view& _name, const InputVarType& _var) const { if constexpr (_i < size_) { using FieldType = std::tuple_element_t<_i, typename ViewType::Fields>; using OriginalType = std::remove_cvref_t; using CurrentType = std::remove_cvref_t>; constexpr auto current_name = FieldType::name(); if (!std::get<_i>(*found_) && _name == current_name) { auto res = Parser::read(*r_, _var); if (res) { if constexpr (std::is_pointer_v) { move_to(rfl::get<_i>(*view_), &(*res)); } else { rfl::get<_i>(*view_) = *res; } std::get<_i>(*set_) = true; } else { errors_->push_back(Error("Failed to parse field '" + std::string(current_name) + "': " + res.error()->what())); } std::get<_i>(*found_) = true; return; } read<_i + 1>(_name, _var); } } /// Because of the way we have allocated the fields, we need to manually /// trigger the destructors. template void call_destructors_where_necessary() const { if constexpr (_i < size_) { using FieldType = std::tuple_element_t<_i, typename ViewType::Fields>; using OriginalType = std::remove_cvref_t; using ValueType = std::remove_cvref_t>; if constexpr (!std::is_array_v && std::is_pointer_v && std::is_destructible_v) { if (std::get<_i>(*set_)) { rfl::get<_i>(*view_)->~ValueType(); } } else if constexpr (std::is_array_v) { if (std::get<_i>(*set_)) { auto ptr = rfl::get<_i>(*view_); call_destructor_on_array(sizeof(*ptr) / sizeof(**ptr), *ptr); } } call_destructors_where_necessary<_i + 1>(); } } private: template void call_destructor_on_array(const size_t _size, T* _ptr) const { for (size_t i = 0; i < _size; ++i) { if constexpr (std::is_array_v) { call_destructor_on_array(sizeof(*_ptr) / sizeof(**_ptr), *(_ptr + i)); } else if constexpr (std::is_destructible_v) { (_ptr + i)->~T(); } } } template void move_to(Target* _t, Source* _s) const { if constexpr (std::is_const_v) { return move_to(const_cast*>(_t), _s); } else if constexpr (!internal::is_array_v && !std::is_array_v) { ::new (_t) Target(std::move(*_s)); } else if constexpr (internal::is_array_v) { static_assert(std::is_array_v, "Expected target to be a c-array."); for (size_t i = 0; i < _s->arr_.size(); ++i) { move_to(&((*_t)[i]), &(_s->arr_[i])); } } else { for (size_t i = 0; i < _s->size(); ++i) { move_to(&((*_t)[i]), &((*_s)[i])); } } } private: /// The underlying reader. const R* r_; /// The underlying view. ViewType* view_; /// Indicates that a certain field has been found. std::array* found_; /// Indicates that a certain field has been successfully set - necessary, /// because we have to trigger the destructors manually. std::array* set_; /// Collects any errors we may have come across. std::vector* errors_; }; } // namespace rfl::parsing #endif