JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
adapter_for.hpp
Go to the documentation of this file.
1/// \file jsonv/serialization/adapter_for.hpp
2///
3/// Copyright (c) 2015-2026 by Travis Gockel. All rights reserved.
4///
5/// This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
6/// as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
7/// version.
8///
9/// \author Travis Gockel (travis@gockelhut.com)
10#pragma once
11
12#include <jsonv/config.hpp>
15#include <jsonv/value.hpp>
16
17#include <expected>
18#include <new>
19#include <utility>
20
21namespace jsonv
22{
23
24/// \addtogroup Serialization
25/// \{
26
27/// An adapter for the type \c T. This is a utility class which converts the `void*`s used in the \c deserializer and
28/// \c serializer interfaces into the more-friendly \c T.
29///
30/// \see deserializer_for
31/// \see serializer_for
32/// \see value_adapter_for
33template <typename T>
35 public adapter
36{
37public:
38 /// \see deserializer::get_type
39 /// \see serializer::get_type
41 virtual const std::type_info& get_type() const noexcept override
42 {
43 return typeid(T);
44 }
45
46 /// \see deserializer::deserialize
48 virtual std::expected<void, ast_node_type>
49 deserialize(deserialization_context& context, reader& from, void* into) const override
50 {
51 if (auto created = create(context, from))
52 {
53 try
54 {
55 new(into) T(std::move(created).value());
56 }
57 catch (...)
58 {
59 // `create` succeeded, so the cursor is already one past the value this was built from. Moving it
60 // into place is the last thing which can fail, and `T` is the caller's type; a loop recovering from
61 // that failure must not step over the value a second time.
62 context.note_value_consumed(from);
63 throw;
64 }
65 return {};
66 }
67 else
68 {
69 return std::unexpected(created.error());
70 }
71 }
72
73 /// \see serializer::serialize
74 virtual void serialize(const serialization_context& context, const void* from, writer& to) const override
75 {
76 serialize(context, *static_cast<const T*>(from), to);
77 }
78
79protected:
80 /// Create an instance of \c T by reading \a from.
81 ///
82 /// \param context Extra information to help you decode sub-objects, such as looking up other \c deserializer
83 /// implementations via \c formats. Record any problem you encounter with
84 /// \c deserialization_context::problem.
85 /// \param from The JSON \c reader to deserialize from. On a successful return it should sit one position past the
86 /// value which was read, as \c reader::next_value would have left it.
87 ///
88 /// \returns The created instance; otherwise a \c std::unexpected carrying the \c ast_node_type actually found
89 /// when the failure was a type mismatch, or \c ast_node_type::error otherwise.
91 virtual std::expected<T, ast_node_type> create(deserialization_context& context, reader& from) const = 0;
92
93 /// Write \a from into \a to.
94 ///
95 /// \see serializer::serialize
96 virtual void serialize(const serialization_context& context, const T& from, writer& to) const = 0;
97};
98
99/// A base for adapters written against the older \c value -based interface, in both directions.
100///
101/// Reading, the subtree under the reader is materialised with \c read_value and handed to the subclass, whose
102/// \c create runs unchanged. Writing, the \c value the subclass's \c to_json builds is written whole into the writer
103/// with \c writer::write. Both cost the tree the \c reader and the \c writer exist to avoid -- so it is a stepping
104/// stone for ports, not a destination. New adapters should derive from \c adapter_for, walk the \c reader and write
105/// into the \c writer.
106template <typename T>
108 public adapter_for<T>
109{
110protected:
112 virtual std::expected<T, ast_node_type>
113 create(deserialization_context& context, reader& from) const final override
114 {
115 // A deserialization_error thrown by the subclass is deliberately left to propagate:
116 // deserialization_context::deserialize folds its problem list onto the context, which is how the path and
117 // message an older adapter throws with survive into the pipeline's std::expected channel.
118 detail::borrowed_subtree subtree(context, from);
119 T out = create(context, subtree.get());
120
121 // Only once the older body is through: a throw out of it leaves the cursor on the value which failed, so the
122 // problem names that rather than whatever follows it.
123 subtree.commit();
124
125 // Committing steps the cursor past the value, so the return -- which moves the result into the `std::expected`
126 // with the caller's own move constructor -- fails with that value behind it. `borrowed_subtree` cannot say so
127 // on our behalf here: it has committed, which is the state it takes to mean the body succeeded.
128 try
129 {
130 return out;
131 }
132 catch (...)
133 {
134 context.note_value_consumed(from);
135 throw;
136 }
137 }
138
139 virtual void serialize(const serialization_context& context, const T& from, writer& to) const final override
140 {
141 to.write(to_json(context, from));
142 }
143
144 /// Create an instance of \c T from the materialised \a from.
145 ///
146 /// \throws deserialization_error if \a from cannot be converted to a \c T.
148 virtual T create(deserialization_context& context, const value& from) const = 0;
149
150 /// Convert \a from into a \c value.
152 virtual value to_json(const serialization_context& context, const T& from) const = 0;
153};
154
155/// \}
156
157}
Copyright (c) 2015-2020 by Travis Gockel.
An adapter for the type T.
virtual std::expected< T, ast_node_type > create(deserialization_context &context, reader &from) const =0
Create an instance of T by reading from.
virtual const std::type_info & get_type() const noexcept override
virtual void serialize(const serialization_context &context, const T &from, writer &to) const =0
Write from into to.
virtual void serialize(const serialization_context &context, const void *from, writer &to) const override
virtual std::expected< void, ast_node_type > deserialize(deserialization_context &context, reader &from, void *into) const override
An adapter is both a deserializer and a serializer.
Definition adapter.hpp:27
Provides extra information to routines used for deserialization and serialization.
Definition context.hpp:26
Provides extra information to routines used for deserialization, collects the problems they encounter...
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON...
Definition reader.hpp:106
Provides extra information to routines used for serialization: the formats to find other serializers ...
Definition serialize.hpp:42
A base for adapters written against the older value -based interface, in both directions.
virtual value to_json(const serialization_context &context, const T &from) const =0
Convert from into a value.
virtual std::expected< T, ast_node_type > create(deserialization_context &context, reader &from) const final override
Create an instance of T by reading from.
virtual void serialize(const serialization_context &context, const T &from, writer &to) const final override
Write from into to.
virtual T create(deserialization_context &context, const value &from) const =0
Create an instance of T from the materialised from.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:156
A writer instance writes a JSON ast_node sequence to some form of sink: an encoder,...
Definition writer.hpp:68
Copyright (c) 2014-2020 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:143
Serialization of C++ types into a JSON token stream.
Copyright (c) 2012-2020 by Travis Gockel.