@@ -20,32 +20,9 @@ namespace clang {
20
20
namespace mrdocs {
21
21
namespace dom {
22
22
23
- /* * Mapping traits to convert types into dom::Object.
24
-
25
- This class should be specialized by any type that needs to be converted
26
- to/from a @ref dom::Object. For example:
27
-
28
- @code
29
- template<>
30
- struct MappingTraits<MyStruct> {
31
- template <class IO>
32
- static void map(IO &io, MyStruct const& s)
33
- {
34
- io.map("name", s.name);
35
- io.map("size", s.size);
36
- io.map("age", s.age);
37
- }
38
- };
39
- @endcode
40
- */
41
- template <class T >
42
- struct MappingTraits {
43
- // void map(dom::IO &io, T &fields) const;
44
- };
45
-
46
23
namespace detail
47
24
{
48
- /* * A class representing an archetypal IO object.
25
+ /* A class representing an archetypal IO object.
49
26
*/
50
27
struct MRDOCS_DECL ArchetypalIO
51
28
{
@@ -57,15 +34,91 @@ namespace detail
57
34
void
58
35
defer (std::string_view, F const &) const {}
59
36
};
37
+
38
+ /* A helper empty struct
39
+ */
40
+ struct NoLazyObjectContext { };
60
41
}
61
42
62
- // / Concept to determine if @ref MappingTraits is defined for a type T
63
- template <class T >
64
- concept HasMappingTraits = requires(detail::ArchetypalIO& io, T const & o)
43
+ /* * Customization point tag.
44
+
45
+ This tag type is used by the class
46
+ @ref dom::LazyObjectImpl to select overloads
47
+ of `tag_invoke`.
48
+
49
+ @note This type is empty; it has no members.
50
+
51
+ @see @ref dom::LazyObjectImpl
52
+ <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
53
+ tag_invoke: A general pattern for supporting customisable functions</a>
54
+ */
55
+ struct LazyObjectMapTag { };
56
+
57
+ /* * Concept to determine if a type can be mapped to a
58
+ @ref dom::LazyObjectImpl with a user-provided conversion.
59
+
60
+ This concept determines if the user-provided conversion is
61
+ defined as:
62
+
63
+ @code
64
+ template <class IO>
65
+ void tag_invoke( LazyObjectMapTag, IO&, T& );
66
+ @endcode
67
+
68
+ This customization can be defined by any type that needs to be converted
69
+ to/from a lazy @ref dom::Object. For example:
70
+
71
+ @code
72
+ template <class IO>
73
+ void tag_invoke( LazyObjectMapTag, IO& io, MyStruct const& s)
74
+ {
75
+ io.map("name", s.name);
76
+ io.map("size", s.size);
77
+ io.map("age", s.age);
78
+ }
79
+ @endcode
80
+
81
+ */
82
+ template <class T >
83
+ concept HasLazyObjectMapWithoutContext = requires(
84
+ detail::ArchetypalIO& io,
85
+ T const & t)
65
86
{
66
- { std::declval<MappingTraits<T>>(). map ( io, o ) } -> std::same_as<void >;
87
+ { tag_invoke (LazyObjectMapTag{}, io, t ) } -> std::same_as<void >;
67
88
};
68
89
90
+ /* * Concept to determine if a type can be mapped to a
91
+ @ref dom::LazyObjectImpl with a user-provided conversion.
92
+
93
+ This concept determines if the user-provided conversion is
94
+ defined as:
95
+
96
+ @code
97
+ template <class IO>
98
+ void tag_invoke( LazyObjectMapTag, IO&, T, Context const& );
99
+ @endcode
100
+ */
101
+ template <class T , class Context >
102
+ concept HasLazyObjectMapWithContext = requires(
103
+ detail::ArchetypalIO& io,
104
+ T const & t,
105
+ Context const & ctx)
106
+ {
107
+ { tag_invoke (LazyObjectMapTag{}, io, t, ctx) } -> std::same_as<void >;
108
+ };
109
+
110
+ /* * Determine if `T` can be converted to @ref dom::Value.
111
+
112
+ If `T` can be converted to @ref dom::Value via a
113
+ call to @ref dom::ValueFrom, the static data member `value`
114
+ is defined as `true`. Otherwise, `value` is
115
+ defined as `false`.
116
+ */
117
+ template <class T , class Context >
118
+ concept HasLazyObjectMap =
119
+ HasLazyObjectMapWithContext<T, Context> ||
120
+ HasLazyObjectMapWithoutContext<T>;
121
+
69
122
// ------------------------------------------------
70
123
//
71
124
// LazyObjectImpl
@@ -79,37 +132,54 @@ concept HasMappingTraits = requires(detail::ArchetypalIO& io, T const& o)
79
132
as they are accessed.
80
133
81
134
When any of the object properties are accessed,
82
- the object Value is constructed.
135
+ the object @ref dom:: Value is constructed.
83
136
In practice, the object never takes any memory
84
137
besides the pointer to the underlying object.
85
138
86
139
The keys and values in the underlying object
87
- should be mapped using the MappingTraits<T> class .
140
+ should be mapped using `tag_invoke` .
88
141
89
142
This class is typically useful for
90
143
implementing objects that are expensive
91
144
and have recursive dependencies, as these
92
145
recursive dependencies can also be deferred.
93
146
147
+ A context can also be stored in the object
148
+ as a form to customize how the object is
149
+ mapped. This context should be copyable
150
+ and is propagated to other objects that
151
+ support an overload with the same context.
152
+
153
+ The context can be simply a tag
154
+ identifying how to map the object, or
155
+ a more complex object carrying data
156
+ to customize the mapping process.
157
+
158
+ In the latter case, because the context
159
+ should be a copyable, the user might want
160
+ to use a type with reference semantics.
161
+
94
162
*/
95
- template <HasMappingTraits T>
163
+ template <class T , class Context = detail::NoLazyObjectContext>
164
+ requires HasLazyObjectMap<T, Context>
96
165
class LazyObjectImpl : public ObjectImpl
97
166
{
98
167
T const * underlying_;
99
168
Object overlay_;
100
- [[no_unique_address]] MappingTraits<T> traits_ {};
169
+ [[no_unique_address]] Context context_ {};
101
170
102
171
public:
103
172
explicit
104
173
LazyObjectImpl (T const & obj)
105
- requires std::constructible_from<MappingTraits<T> >
174
+ requires HasLazyObjectMapWithoutContext<T >
106
175
: underlying_(&obj)
107
- , traits_ {} {}
176
+ , context_ {} {}
108
177
109
178
explicit
110
- LazyObjectImpl (T const & obj, MappingTraits<T> traits)
179
+ LazyObjectImpl (T const & obj, Context const & context)
180
+ requires HasLazyObjectMapWithContext<T, Context>
111
181
: underlying_(&obj)
112
- , traits_(std::move(traits) ) {}
182
+ , context_(context ) {}
113
183
114
184
~LazyObjectImpl () override = default ;
115
185
@@ -199,9 +269,10 @@ namespace detail
199
269
};
200
270
}
201
271
202
- template <HasMappingTraits T>
272
+ template <class T , class Context >
273
+ requires HasLazyObjectMap<T, Context>
203
274
std::size_t
204
- LazyObjectImpl<T>::
275
+ LazyObjectImpl<T, Context >::
205
276
size () const
206
277
{
207
278
std::size_t result;
@@ -210,13 +281,21 @@ size() const
210
281
{
211
282
result += !overlay_.exists (name);
212
283
});
213
- traits_.map (io, *underlying_);
284
+ if constexpr (HasLazyObjectMapWithContext<T, Context>)
285
+ {
286
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_, context_);
287
+ }
288
+ else
289
+ {
290
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_);
291
+ }
214
292
return result + overlay_.size ();
215
293
}
216
294
217
- template <HasMappingTraits T>
295
+ template <class T , class Context >
296
+ requires HasLazyObjectMap<T, Context>
218
297
bool
219
- LazyObjectImpl<T>::
298
+ LazyObjectImpl<T, Context >::
220
299
exists (std::string_view key) const
221
300
{
222
301
if (overlay_.exists (key))
@@ -232,14 +311,22 @@ exists(std::string_view key) const
232
311
result = true ;
233
312
}
234
313
});
235
- traits_.map (io, *underlying_);
314
+ if constexpr (HasLazyObjectMapWithContext<T, Context>)
315
+ {
316
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_, context_);
317
+ }
318
+ else
319
+ {
320
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_);
321
+ }
236
322
return result;
237
323
}
238
324
239
325
240
- template <HasMappingTraits T>
326
+ template <class T , class Context >
327
+ requires HasLazyObjectMap<T, Context>
241
328
Value
242
- LazyObjectImpl<T>::
329
+ LazyObjectImpl<T, Context >::
243
330
get (std::string_view key) const
244
331
{
245
332
if (overlay_.exists (key))
@@ -261,21 +348,30 @@ get(std::string_view key) const
261
348
ValueFrom (deferred (), result);
262
349
}
263
350
});
264
- traits_.map (io, *underlying_);
351
+ if constexpr (HasLazyObjectMapWithContext<T, Context>)
352
+ {
353
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_, context_);
354
+ }
355
+ else
356
+ {
357
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_);
358
+ }
265
359
return result;
266
360
}
267
361
268
- template <HasMappingTraits T>
362
+ template <class T , class Context >
363
+ requires HasLazyObjectMap<T, Context>
269
364
void
270
- LazyObjectImpl<T>::
365
+ LazyObjectImpl<T, Context >::
271
366
set (String key, Value value)
272
367
{
273
368
overlay_.set (std::move (key), std::move (value));
274
369
}
275
370
276
- template <HasMappingTraits T>
371
+ template <class T , class Context >
372
+ requires HasLazyObjectMap<T, Context>
277
373
bool
278
- LazyObjectImpl<T>::
374
+ LazyObjectImpl<T, Context >::
279
375
visit (std::function<bool (String, Value)> fn) const
280
376
{
281
377
bool visitMore = true ;
@@ -293,11 +389,37 @@ visit(std::function<bool(String, Value)> fn) const
293
389
visitMore = fn (name, dom::ValueFrom (deferred ()));
294
390
}
295
391
});
296
- traits_.map (io, *underlying_);
392
+ if constexpr (HasLazyObjectMapWithContext<T, Context>)
393
+ {
394
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_, context_);
395
+ }
396
+ else
397
+ {
398
+ tag_invoke (LazyObjectMapTag{}, io, *underlying_);
399
+ }
297
400
return visitMore && overlay_.visit (fn);
298
401
}
299
402
300
403
404
+ /* * Return a new dom::Object based on a lazy object implementation.
405
+ */
406
+ template <HasLazyObjectMapWithoutContext T>
407
+ Object
408
+ LazyObject (T const & obj)
409
+ {
410
+ return newObject<LazyObjectImpl<T>>(obj);
411
+ }
412
+
413
+ /* * Return a new dom::Object based on a transformed lazy array implementation.
414
+ */
415
+ template <class T , class Context >
416
+ requires HasLazyObjectMap<T, Context>
417
+ Object
418
+ LazyObject (T const & arr, Context const & context)
419
+ {
420
+ return newObject<LazyObjectImpl<T, Context>>(arr, context);
421
+ }
422
+
301
423
} // dom
302
424
} // mrdocs
303
425
} // clang
0 commit comments