@@ -20,32 +20,9 @@ namespace clang {
2020namespace mrdocs {
2121namespace dom {
2222
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-
4623namespace detail
4724{
48- /* * A class representing an archetypal IO object.
25+ /* A class representing an archetypal IO object.
4926 */
5027 struct MRDOCS_DECL ArchetypalIO
5128 {
@@ -57,15 +34,91 @@ namespace detail
5734 void
5835 defer (std::string_view, F const &) const {}
5936 };
37+
38+ /* A helper empty struct
39+ */
40+ struct NoLazyObjectContext { };
6041}
6142
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)
6586{
66- { std::declval<MappingTraits<T>>(). map ( io, o ) } -> std::same_as<void >;
87+ { tag_invoke (LazyObjectMapTag{}, io, t ) } -> std::same_as<void >;
6788};
6889
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+
69122// ------------------------------------------------
70123//
71124// LazyObjectImpl
@@ -79,37 +132,54 @@ concept HasMappingTraits = requires(detail::ArchetypalIO& io, T const& o)
79132 as they are accessed.
80133
81134 When any of the object properties are accessed,
82- the object Value is constructed.
135+ the object @ref dom:: Value is constructed.
83136 In practice, the object never takes any memory
84137 besides the pointer to the underlying object.
85138
86139 The keys and values in the underlying object
87- should be mapped using the MappingTraits<T> class .
140+ should be mapped using `tag_invoke` .
88141
89142 This class is typically useful for
90143 implementing objects that are expensive
91144 and have recursive dependencies, as these
92145 recursive dependencies can also be deferred.
93146
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+
94162*/
95- template <HasMappingTraits T>
163+ template <class T , class Context = detail::NoLazyObjectContext>
164+ requires HasLazyObjectMap<T, Context>
96165class LazyObjectImpl : public ObjectImpl
97166{
98167 T const * underlying_;
99168 Object overlay_;
100- [[no_unique_address]] MappingTraits<T> traits_ {};
169+ [[no_unique_address]] Context context_ {};
101170
102171public:
103172 explicit
104173 LazyObjectImpl (T const & obj)
105- requires std::constructible_from<MappingTraits<T> >
174+ requires HasLazyObjectMapWithoutContext<T >
106175 : underlying_(&obj)
107- , traits_ {} {}
176+ , context_ {} {}
108177
109178 explicit
110- LazyObjectImpl (T const & obj, MappingTraits<T> traits)
179+ LazyObjectImpl (T const & obj, Context const & context)
180+ requires HasLazyObjectMapWithContext<T, Context>
111181 : underlying_(&obj)
112- , traits_(std::move(traits) ) {}
182+ , context_(context ) {}
113183
114184 ~LazyObjectImpl () override = default ;
115185
@@ -199,9 +269,10 @@ namespace detail
199269 };
200270}
201271
202- template <HasMappingTraits T>
272+ template <class T , class Context >
273+ requires HasLazyObjectMap<T, Context>
203274std::size_t
204- LazyObjectImpl<T>::
275+ LazyObjectImpl<T, Context >::
205276size () const
206277{
207278 std::size_t result;
@@ -210,13 +281,21 @@ size() const
210281 {
211282 result += !overlay_.exists (name);
212283 });
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+ }
214292 return result + overlay_.size ();
215293}
216294
217- template <HasMappingTraits T>
295+ template <class T , class Context >
296+ requires HasLazyObjectMap<T, Context>
218297bool
219- LazyObjectImpl<T>::
298+ LazyObjectImpl<T, Context >::
220299exists (std::string_view key) const
221300{
222301 if (overlay_.exists (key))
@@ -232,14 +311,22 @@ exists(std::string_view key) const
232311 result = true ;
233312 }
234313 });
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+ }
236322 return result;
237323}
238324
239325
240- template <HasMappingTraits T>
326+ template <class T , class Context >
327+ requires HasLazyObjectMap<T, Context>
241328Value
242- LazyObjectImpl<T>::
329+ LazyObjectImpl<T, Context >::
243330get (std::string_view key) const
244331{
245332 if (overlay_.exists (key))
@@ -261,21 +348,30 @@ get(std::string_view key) const
261348 ValueFrom (deferred (), result);
262349 }
263350 });
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+ }
265359 return result;
266360}
267361
268- template <HasMappingTraits T>
362+ template <class T , class Context >
363+ requires HasLazyObjectMap<T, Context>
269364void
270- LazyObjectImpl<T>::
365+ LazyObjectImpl<T, Context >::
271366set (String key, Value value)
272367{
273368 overlay_.set (std::move (key), std::move (value));
274369}
275370
276- template <HasMappingTraits T>
371+ template <class T , class Context >
372+ requires HasLazyObjectMap<T, Context>
277373bool
278- LazyObjectImpl<T>::
374+ LazyObjectImpl<T, Context >::
279375visit (std::function<bool (String, Value)> fn) const
280376{
281377 bool visitMore = true ;
@@ -293,11 +389,37 @@ visit(std::function<bool(String, Value)> fn) const
293389 visitMore = fn (name, dom::ValueFrom (deferred ()));
294390 }
295391 });
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+ }
297400 return visitMore && overlay_.visit (fn);
298401}
299402
300403
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+
301423} // dom
302424} // mrdocs
303425} // clang
0 commit comments