Skip to content

Commit

Permalink
refactor: lazy object mapping traits
Browse files Browse the repository at this point in the history
fix #696
  • Loading branch information
alandefreitas committed Oct 28, 2024
1 parent 9daf71f commit 9d30ea1
Show file tree
Hide file tree
Showing 20 changed files with 1,148 additions and 681 deletions.
74 changes: 2 additions & 72 deletions include/mrdocs/Dom/Object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco ([email protected])
// Copyright (c) 2024 Alan de Freitas ([email protected])
//
// Official repository: https://github.com/cppalliance/mrdocs
//
Expand Down Expand Up @@ -268,7 +269,7 @@ class MRDOCS_DECL
template <class F>
requires
std::invocable<F, String, Value> &&
detail::isExpected<std::invoke_result_t<F, String, Value>>
mrdocs::detail::isExpected<std::invoke_result_t<F, String, Value>>
Expected<void, typename std::invoke_result_t<F, String, Value>::error_type>
visit(F&& fn) const;

Expand Down Expand Up @@ -414,77 +415,6 @@ class MRDOCS_DECL
storage_type entries_;
};

//------------------------------------------------
//
// LazyObjectImpl
//
//------------------------------------------------

/** Abstract lazy object interface.
This interface is used to define objects
that are constructed on demand.
The subclass must override the `construct`
function to return the constructed object.
It will typically also store whatever
data is necessary to construct this object.
When any of the object properties are accessed
for the first time, the object is constructed.
This can happen via any of the public functions,
such as `get`, `set`, `size`, `exists`, or `visit`.
The underlying object storage is only
initialized when the first property is
set or accessed. In practice, it means
the object is never initialized if it's
not used in a template.
When the object is initialized, the
*/
class MRDOCS_DECL
LazyObjectImpl : public ObjectImpl
{
#ifdef __cpp_lib_atomic_shared_ptr
std::atomic<std::shared_ptr<ObjectImpl>> mutable sp_;
#else
std::shared_ptr<ObjectImpl> mutable sp_;
#endif

using impl_type = Object::impl_type;

/* Return the constructed object.
This function is invoked by all public
functions that access the object properties.
When invoked for the first time, the object
is constructed and stored in the shared
pointer.
Further invocations return a reference
to the existing value in the shared pointer.
*/
ObjectImpl& obj() const;

protected:
/** Return the constructed object.
Subclasses override this.
The function is invoked just in time.
*/
virtual Object construct() const = 0;

public:
std::size_t size() const override;
Value get(std::string_view key) const override;
void set(String key, Value value) override;
bool visit(std::function<bool(String, Value)>) const override;
bool exists(std::string_view key) const override;
};

} // dom
} // mrdocs
} // clang
Expand Down
2 changes: 1 addition & 1 deletion include/mrdocs/Dom/Object.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ void Object::visit(F&& fn) const
template <class F>
requires
std::invocable<F, String, Value> &&
detail::isExpected<std::invoke_result_t<F, String, Value>>
mrdocs::detail::isExpected<std::invoke_result_t<F, String, Value>>
Expected<void, typename std::invoke_result_t<F, String, Value>::error_type>
Object::visit(F&& fn) const
{
Expand Down
87 changes: 69 additions & 18 deletions include/mrdocs/Dom/Value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,36 @@ safeString(dom::Value const& str);

namespace dom {

/** Mapping traits to convert types into dom::Object.
This class should be specialized by any type that needs to be converted
to/from a @ref dom::Object. For example:
@code
template<>
struct MappingTraits<MyStruct> {
template <class IO>
static void map(IO &io, MyStruct const& s)
{
io.map("name", s.name);
io.map("size", s.size);
io.map("age", s.age);
}
};
@endcode
*/
template<class T>
struct ToValue {
// Value operator()(T const& o) const;
};

/// Concept to determine if @ref ToValue is defined for a type T
template <class T>
concept HasToValue = requires(T const& o)
{
{ Value(std::declval<ToValue<T>>()(o)) } -> std::same_as<Value>;
};

/** A variant container for any kind of Dom value.
*/
class MRDOCS_DECL
Expand Down Expand Up @@ -90,30 +120,38 @@ class MRDOCS_DECL

template<class F>
requires
function_traits_convertible_to_value<F>
function_traits_convertible_to_value<F> &&
(!HasToValue<F>)
Value(F const& f)
: Value(Function(f))
{}

template<class Boolean>
requires std::is_same_v<Boolean, bool>
template<std::same_as<bool> Boolean>
requires (!HasToValue<Boolean>)
Value(Boolean const& b) noexcept
: kind_(Kind::Boolean)
, b_(b)
{
}

template <std::integral T>
requires (!std::same_as<T, bool> && !std::same_as<T, char>)
requires
(!std::same_as<T, bool>) &&
(!std::same_as<T, char>) &&
(!HasToValue<T>)
Value(T v) noexcept : Value(std::int64_t(v)) {}

template <std::floating_point T>
requires (!HasToValue<T>)
Value(T v) noexcept : Value(std::int64_t(v)) {}

Value(char c) noexcept : Value(std::string_view(&c, 1)) {}

template<class Enum>
requires std::is_enum_v<Enum> && (!std::same_as<Enum, dom::Kind>)
requires
std::is_enum_v<Enum> &&
(!std::same_as<Enum, dom::Kind>) &&
(!HasToValue<Enum>)
Value(Enum v) noexcept
: Value(static_cast<std::underlying_type_t<Enum>>(v))
{}
Expand All @@ -131,6 +169,7 @@ class MRDOCS_DECL
}

template <std::convertible_to<String> StringLike>
requires (!HasToValue<StringLike>)
Value(StringLike const& s)
: Value(String(s))
{
Expand All @@ -155,6 +194,12 @@ class MRDOCS_DECL
{
}

template <HasToValue T>
Value(T const& t)
: Value(ToValue<T>{}(t))
{
}

Value& operator=(Value const& other);
Value& operator=(Value&& other) noexcept;

Expand Down Expand Up @@ -425,29 +470,35 @@ class MRDOCS_DECL
Value const& lhs,
Value const& rhs) noexcept;

/** Compare two values for inequality.
*/
friend
std::strong_ordering
operator<=>(
Value const& lhs,
Value const& rhs) noexcept;

/// @overload
template <std::convertible_to<Value> S>
friend auto operator==(
S const& lhs, Value const& rhs) noexcept
friend
auto
operator<=>(
S const& lhs,
Value const& rhs) noexcept
{
return Value(lhs) == rhs;
return Value(lhs) <=> rhs;
}

/// @overload
template <std::convertible_to<Value> S>
friend auto operator==(
Value const& lhs, S const& rhs) noexcept
{
return lhs == Value(rhs);
}

/** Compare two values for inequality.
*/
friend
std::strong_ordering
auto
operator<=>(
Value const& lhs,
Value const& rhs) noexcept;
S const& rhs) noexcept
{
return lhs <=> Value(rhs);
}

/** Add or concatenate two values.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
//
// Copyright (c) 2023 Vinnie Falco ([email protected])
// Copyright (c) 2023 Krystian Stasiowski ([email protected])
// Copyright (c) 2024 Alan de Freitas ([email protected])
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#ifndef MRDOCS_API_DOM_DOMMETADATA_HPP
#define MRDOCS_API_DOM_DOMMETADATA_HPP
#ifndef MRDOCS_API_DOM_DOMCORPUS_HPP
#define MRDOCS_API_DOM_DOMCORPUS_HPP

#include <mrdocs/Platform.hpp>
#include <mrdocs/Corpus.hpp>
Expand All @@ -24,9 +25,14 @@ namespace mrdocs {

/** Front-end factory for producing Dom nodes.
A @ref Generator subclasses this object and
then uses it to create the Dom nodes used for
rendering in template engines.
This class keeps a reference to the @ref Corpus
of extracted metadata, and provides a mechanism
for constructing DOM nodes representing the metadata.
A @ref Generator subclasses this object
(e.g. @ref AdocCorpus and @ref HTMLCorpus),
then uses it to create the Dom nodes used
as input for rendering template engines.
*/
class MRDOCS_DECL
DomCorpus
Expand Down Expand Up @@ -62,7 +68,7 @@ class MRDOCS_DECL
*/
Corpus const* operator->() const;

/** Construct a Dom object representing the given symbol.
/** Construct a lazy Dom object representing the specified symbol.
This function is called internally when a `dom::Object`
representing a symbol needs to be constructed because
Expand Down
5 changes: 2 additions & 3 deletions include/mrdocs/Metadata/Symbols.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ namespace mrdocs {
*/
class SymbolID
{
std::uint8_t data_[20]{};

public:
static const SymbolID invalid;
static const SymbolID global;
Expand Down Expand Up @@ -120,9 +122,6 @@ class SymbolID
*/
bool operator==(
const SymbolID& other) const noexcept = default;

private:
value_type data_[20]{};
};

/** The invalid Symbol ID.
Expand Down
1 change: 0 additions & 1 deletion include/mrdocs/mrdocs.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@

<Type Name="clang::mrdocs::dom::Object">
<Intrinsic Name="as_default" Expression="dynamic_cast&lt;DefaultObjectImpl*&gt;(impl_._Ptr)"/>
<Intrinsic Name="as_lazy" Expression="dynamic_cast&lt;LazyObjectImpl*&gt;(impl_._Ptr)"/>
<Intrinsic Name="as_overlay" Expression="dynamic_cast&lt;OverlayObjectImpl*&gt;(impl_._Ptr)"/>
<Intrinsic Name="as_hbs_helper" Expression="dynamic_cast&lt;HbsHelperObjectImpl*&gt;(impl_._Ptr)"/>
<DisplayString>[ dom::Object ]</DisplayString>
Expand Down
Loading

0 comments on commit 9d30ea1

Please sign in to comment.