diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index aaa10d293..5eedb0fe9 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -119,8 +119,7 @@ class MRDOCS_VISIBLE template requires std::derived_from T const& - get( - SymbolID const& id) const noexcept; + get(SymbolID const& id) const noexcept; /** Return the metadata for the global namespace. @@ -160,18 +159,23 @@ class MRDOCS_VISIBLE } } - /** Visit the function members of specified Info. + /** Visit the member overloads of specified ScopeInfo. - This function iterates the members of the specified - ScopeInfo `S`. For each member associated with a - function with the same name as the member, the - function object `f` is invoked with the member + This function iterates the members of the + specified ScopeInfo `S`. + + For each member in the scope, we check + if the member is a function with overloads. + + If the member is a function with overloads, + we create an @ref OverloadSet object and invoke + the function object `f` with the @ref OverloadSet as the first argument, followed by `args...`. - When there are more than one member function - with the same name, the function object `f` is - invoked with an @ref OverloadSet as the first - argument, followed by `args...`. + If the member is not a function with overloads, + we invoke the function object `f` with the @ref Info + member as the first argument, followed by `args...`. + */ template void traverseOverloads( @@ -230,6 +234,7 @@ traverseOverloads( ScopeInfo const& S, F&& f, Args&&... args) const { + MRDOCS_ASSERT(S.Members.empty() == S.Lookups.empty()); for(const SymbolID& id : S.Members) { const Info& member = get(id); @@ -244,8 +249,10 @@ traverseOverloads( return get(elem).isFunction(); #endif }); - if(lookup.size() == 1 || - first_func == lookup.end()) + bool const nonOverloadedFunction = lookup.size() == 1; + bool const notFunction = first_func == lookup.end(); + if(nonOverloadedFunction || + notFunction) { visit(member, std::forward(f), std::forward(args)...); diff --git a/include/mrdocs/Dom/Value.hpp b/include/mrdocs/Dom/Value.hpp index bd0f793c4..e05686c91 100644 --- a/include/mrdocs/Dom/Value.hpp +++ b/include/mrdocs/Dom/Value.hpp @@ -54,36 +54,6 @@ 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 { - template - 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 -struct ToValue { - // Value operator()(T const& o) const; -}; - -/// Concept to determine if @ref ToValue is defined for a type T -template -concept HasToValue = requires(T const& o) -{ - { Value(std::declval>()(o)) } -> std::same_as; -}; - /** A variant container for any kind of Dom value. */ class MRDOCS_DECL @@ -120,14 +90,12 @@ class MRDOCS_DECL template requires - function_traits_convertible_to_value && - (!HasToValue) + function_traits_convertible_to_value Value(F const& f) : Value(Function(f)) {} template Boolean> - requires (!HasToValue) Value(Boolean const& b) noexcept : kind_(Kind::Boolean) , b_(b) @@ -137,25 +105,14 @@ class MRDOCS_DECL template requires (!std::same_as) && - (!std::same_as) && - (!HasToValue) + (!std::same_as) Value(T v) noexcept : Value(std::int64_t(v)) {} template - requires (!HasToValue) Value(T v) noexcept : Value(std::int64_t(v)) {} Value(char c) noexcept : Value(std::string_view(&c, 1)) {} - template - requires - std::is_enum_v && - (!std::same_as) && - (!HasToValue) - Value(Enum v) noexcept - : Value(static_cast>(v)) - {} - template Value(char const(&sz)[N]) : Value(String(sz)) @@ -169,7 +126,6 @@ class MRDOCS_DECL } template StringLike> - requires (!HasToValue) Value(StringLike const& s) : Value(String(s)) { @@ -194,12 +150,6 @@ class MRDOCS_DECL { } - template - Value(T const& t) - : Value(ToValue{}(t)) - { - } - Value& operator=(Value const& other); Value& operator=(Value&& other) noexcept; @@ -607,13 +557,280 @@ stringOrNull( { if(!s.empty()) { - return s; + return {s}; } return nullptr; } //------------------------------------------------ +/** Customization point tag. + + This tag type is used by the function + @ref dom::ValueFrom to select overloads + of `tag_invoke`. + + @note This type is empty; it has no members. + + @see @ref dom::ValueFrom + + tag_invoke: A general pattern for supporting customisable functions +*/ +struct ValueFromTag { }; + +/** Concept to determine if a type can be converted to a @ref dom::Value + with a user-provided conversion. + + This concept determines if the user-provided conversion is + defined as: + + @code + void tag_invoke( ValueFromTag, dom::Value&, T ); + @endcode + */ +template +concept HasValueFromWithoutContext = requires( + Value& v, + T const& t) +{ + tag_invoke(ValueFromTag{}, v, t); +}; + +/** Concept to determine if a type can be converted to a @ref dom::Value + with a user-provided conversion. + + This concept determines if the user-provided conversion is + defined as: + + @code + void tag_invoke( ValueFromTag, dom::Value&, T, Context const& ); + @endcode + */ +template +concept HasValueFromWithContext = requires( + Value& v, + T const& t, + Context const& ctx) +{ + tag_invoke(ValueFromTag{}, v, t, ctx); +}; + +/** Determine if `T` can be converted to @ref dom::Value. + + If `T` can be converted to @ref dom::Value via a + call to @ref dom::ValueFrom, the static data member `value` + is defined as `true`. Otherwise, `value` is + defined as `false`. + + @see @ref dom::ValueFrom +*/ +template +concept HasValueFrom = + HasValueFromWithContext || + HasValueFromWithoutContext || + std::constructible_from; + +/** Determine if ` T` can be converted to @ref dom::Value + without a context. + + This concept determines if there is a user-provided + conversion to @ref dom::Value that does not require + a context or if @ref dom::Value has a constructor + that can be used to convert `T` to a @ref dom::Value. + */ +template +concept HasStandaloneValueFrom = + HasValueFromWithoutContext || + std::constructible_from; + +/** Convert an object of type `T` to @ref dom::Value. + + This function attempts to convert an object + of type `T` to @ref dom::Value using + + @li a user-provided overload of `tag_invoke`. + + @li one of @ref dom::Value's constructors, + + Conversion of user-provided types is done by calling an overload of + `tag_invoke` found by argument-dependent lookup. Its signature should + be similar to: + + @code + void tag_invoke( ValueFromTag, dom::Value&, T, Context const& ); + @endcode + + or + + @code + void tag_invoke( ValueFromTag, dom::Value&, T ); + @endcode + + The overloads are checked for existence in that order and the first that + matches will be selected. + + The `ctx` argument can be used either as a tag type to provide conversions + for third-party types, or to pass extra data to the conversion function. + + @par Exception Safety + Strong guarantee. + + @tparam T The type of the object to convert. + + @tparam Context The type of context passed to the conversion function. + + @param t The object to convert. + + @param ctx Context passed to the conversion function. + + @param jv @ref dom::Value out parameter. + + @see @ref dom::ValueFromTag + + tag_invoke: A general pattern for supporting customisable functions +*/ +template T> +void +ValueFrom( + T&& t, + Context const& ctx, + Value& v) +{ + using BT = std::remove_cvref_t; + if constexpr (HasValueFromWithContext) + { + tag_invoke(ValueFromTag{}, v, static_cast(t), ctx); + } + else { + ValueFrom(static_cast(t), v); + } +} + +/** Convert an object of type `T` to @ref dom::Value. + + This function attempts to convert an object + of type `T` to @ref dom::Value using + + @li a user-provided overload of `tag_invoke`. + + @li one of @ref dom::Value's constructors, + + Conversion of other types is done by calling an overload of `tag_invoke` + found by argument-dependent lookup. Its signature should be similar to: + + @code + void tag_invoke( ValueFromTag, dom::Value&, T ); + @endcode + + @par Exception Safety + Strong guarantee. + + @tparam T The type of the object to convert. + + @param t The object to convert. + + @param jv @ref dom::Value out parameter. + + @see @ref dom::ValueFromTag + + tag_invoke: A general pattern for supporting customisable functions +*/ +template +requires HasStandaloneValueFrom +void +ValueFrom( + T&& t, + Value& v) +{ + using BT = std::remove_cvref_t; + if constexpr (HasValueFromWithoutContext) + { + tag_invoke(ValueFromTag{}, v, static_cast(t)); + } + else /* if constexpr (std::constructible_from) */ + { + v = Value(static_cast(t)); + } +} + +/** Convert an object of type `T` to @ref dom::Value. + + This function attempts to convert an object + of type `T` to @ref dom::Value using + + @li a user-provided overload of `tag_invoke`. + + @li one of @ref dom::Value's constructors, + + Conversion of other types is done by calling an overload of `tag_invoke` + found by argument-dependent lookup. Its signature should be similar to: + + @code + void tag_invoke( ValueFromTag, dom::Value&, T ); + @endcode + + @par Exception Safety + Strong guarantee. + + @tparam T The type of the object to convert. + + @param t The object to convert. + + @return @ref dom::Value out parameter. + + @see @ref dom::ValueFromTag, + + tag_invoke: A general pattern for supporting customisable functions +*/ +template +requires HasStandaloneValueFrom +Value +ValueFrom(T&& t) +{ + dom::Value v; + ValueFrom(static_cast(t), v); + return v; +} + +/** Convert an object of type `T` to @ref dom::Value with a context + + This function attempts to convert an object + of type `T` to @ref dom::Value using + + @li a user-provided overload of `tag_invoke`. + + @li one of @ref dom::Value's constructors, + + Conversion of other types is done by calling an overload of `tag_invoke` + found by argument-dependent lookup. Its signature should be similar to: + + @code + void tag_invoke( ValueFromTag, dom::Value&, T ); + @endcode + + @par Exception Safety + Strong guarantee. + + @tparam T The type of the object to convert. + + @param t The object to convert. + + @return @ref dom::Value out parameter. + + @see @ref dom::ValueFromTag, + + tag_invoke: A general pattern for supporting customisable functions +*/ +template +requires HasValueFrom +Value +ValueFrom(T&& t, Context const& ctx) +{ + dom::Value v; + ValueFrom(static_cast(t), ctx, v); + return v; +} + } // dom template SV> diff --git a/include/mrdocs/Metadata/DomCorpus.hpp b/include/mrdocs/Metadata/DomCorpus.hpp index f6b3348dc..d9207285c 100644 --- a/include/mrdocs/Metadata/DomCorpus.hpp +++ b/include/mrdocs/Metadata/DomCorpus.hpp @@ -62,11 +62,17 @@ class MRDOCS_DECL /** Returns the Corpus associated with the Dom. */ - Corpus const& operator*() const; + Corpus const& operator*() const + { + return getCorpus(); + } /** Returns the Corpus associated with the Dom. */ - Corpus const* operator->() const; + Corpus const* operator->() const + { + return &getCorpus(); + } /** Construct a lazy Dom object representing the specified symbol. diff --git a/include/mrdocs/Metadata/Function.hpp b/include/mrdocs/Metadata/Function.hpp index c59515b5c..d29145b6c 100644 --- a/include/mrdocs/Metadata/Function.hpp +++ b/include/mrdocs/Metadata/Function.hpp @@ -67,6 +67,19 @@ enum class FunctionClass MRDOCS_DECL dom::String toString(FunctionClass kind) noexcept; +/** Return the FunctionClass from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FunctionClass kind) +{ + v = toString(kind); +} + + // KRYSTIAN TODO: attributes (nodiscard, deprecated, and carries_dependency) // KRYSTIAN TODO: flag to indicate whether this is a function parameter pack /** Represents a single function parameter */ @@ -97,6 +110,16 @@ struct Param } }; +/** Return the Param as a @ref dom::Value object. + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Param const& p, + DomCorpus const* domCorpus); + // TODO: Expand to allow for documenting templating and default args. // Info for functions. struct FunctionInfo diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp index b2686ff1b..a9c99b6ee 100644 --- a/include/mrdocs/Metadata/Info.hpp +++ b/include/mrdocs/Metadata/Info.hpp @@ -47,6 +47,18 @@ MRDOCS_DECL dom::String toString(InfoKind kind) noexcept; +/** Return the InfoKind from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + InfoKind kind) +{ + v = toString(kind); +} + /** Base class with common properties of all symbols */ struct MRDOCS_VISIBLE @@ -203,6 +215,17 @@ concept InfoParent = requires(T const& t) { requires std::convertible_to, SymbolID const&>; }; +/** Return the Info to a @ref dom::Value object. + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Info const& I, + DomCorpus const* domCorpus); + + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Interface.hpp b/include/mrdocs/Metadata/Interface.hpp index 93016fcbf..fd6b31c11 100644 --- a/include/mrdocs/Metadata/Interface.hpp +++ b/include/mrdocs/Metadata/Interface.hpp @@ -24,51 +24,123 @@ namespace clang { namespace mrdocs { /** A group of children that have the same access specifier. + + This struct represents a collection of symbols that share + the same access specifier within a scope. + + It includes one vector for each info type, + and individual vectors for static functions, types, + and overloads. + + The tranche is not part of the Corpus. It is a temporary + structure generated to aggregate the symbols of a scope. This + structure is provided to the user via the DOM. */ struct Tranche { #define INFO(Type) std::vector Type; #include + /// The types with the same access specifier in a scope. std::vector Types; + + /// The static functions with the same access specifier in a scope. std::vector StaticFunctions; + /// The overloads with the same access specifier in a scope. ScopeInfo Overloads; + + /// The static overloads with the same access specifier in a scope. ScopeInfo StaticOverloads; }; +/** Return a tranche representing the members of a namespace. + + @return The tranche. + + @param Derived The namespace to build the tranche for. + + @param corpus The complete metadata. +*/ +MRDOCS_DECL +Tranche +makeTranche( + NamespaceInfo const& Namespace, + Corpus const& corpus); + +/** Return the Tranche as a @ref dom::Value object. + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::shared_ptr const& sp, + DomCorpus const* domCorpus); + /** The aggregated interface for a given struct, class, or union. + + This class represents the public, protected, and private + interfaces of a record. It is used to generate the + "interface" value of the DOM for symbols that represent + records or namespaces. + + The interface is not part of the Corpus. It is a temporary + structure generated to aggregate the symbols of a record. + This structure is provided to the user via the DOM. + + While the members of a Namespace are directly represented + with a Tranche, the members of a Record are represented + with an Interface. + */ class Interface { public: + /// The corpus containing the complete metadata. Corpus const& corpus; /** The aggregated public interfaces. - */ + + This tranche contains all public members of a record + or namespace. + + */ std::shared_ptr Public; /** The aggregated protected interfaces. - */ + + This tranche contains all protected members of a record + or namespace. + + */ std::shared_ptr Protected; /** The aggregated private interfaces. - */ + + This tranche contains all private members of a record + or namespace. + + */ std::shared_ptr Private; + /** Creates an Interface object for a given record. + + @param I The record to create the interface for. + @param corpus The complete metadata. + @return The interface. + */ MRDOCS_DECL friend Interface makeInterface( - RecordInfo const& Derived, + RecordInfo const& I, Corpus const& corpus); private: explicit Interface(Corpus const&) noexcept; }; -//------------------------------------------------ - /** Return the composite interface for a record. @return The interface. @@ -82,22 +154,18 @@ class Interface MRDOCS_DECL Interface makeInterface( - RecordInfo const& Derived, + RecordInfo const& I, Corpus const& corpus); -/** Return a tranche representing the members of a namespace. - - @return The tranche. - - @param Derived The namespace to build the tranche for. - - @param corpus The complete metadata. -*/ +/** Return the Tranche as a @ref dom::Value object. + */ MRDOCS_DECL -Tranche -makeTranche( - NamespaceInfo const& Namespace, - Corpus const& corpus); +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::shared_ptr const& sp, + DomCorpus const* domCorpus); } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Javadoc.hpp b/include/mrdocs/Metadata/Javadoc.hpp index fe81182e1..0fd51a107 100644 --- a/include/mrdocs/Metadata/Javadoc.hpp +++ b/include/mrdocs/Metadata/Javadoc.hpp @@ -1019,6 +1019,16 @@ class MRDOCS_DECL doc::List blocks_; }; +/** Return the Javadoc as a @ref dom::Value. + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Javadoc const& I, + DomCorpus const* domCorpus); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Name.hpp b/include/mrdocs/Metadata/Name.hpp index 15d100ef3..795e59450 100644 --- a/include/mrdocs/Metadata/Name.hpp +++ b/include/mrdocs/Metadata/Name.hpp @@ -113,6 +113,31 @@ MRDOCS_DECL std::string toString(const NameInfo& N); +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NameInfo const& I, + DomCorpus const* domCorpus); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& I, + DomCorpus const* domCorpus) +{ + if(! I) + { + v = nullptr; + return; + } + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Overloads.hpp b/include/mrdocs/Metadata/Overloads.hpp index dd83a3dda..42b7a3037 100644 --- a/include/mrdocs/Metadata/Overloads.hpp +++ b/include/mrdocs/Metadata/Overloads.hpp @@ -20,16 +20,45 @@ namespace clang { namespace mrdocs { +/** Represents a set of function overloads. + + Each `ScopeInfo`, such as `NamespaceInfo` and `RecordInfo`, + contains a list of member IDs that are part of that scope + and a lookup map of symbols with the same name that are accessible + from that scope. + + By collecting the symbols for a given name from the lookup tables + of all scopes in the namespace, we can create an `OverloadSet`. + + Besides the members of that scope, the `OverloadSet` also contains + the original namespace and the parent of the overload set. This + makes the OverloadSet similar to other `Info` classes, but it + is not part of the corpus and is not directly accessible from + the corpus: it needs to be constructed from the corpus. + + */ struct OverloadSet { + /// The name of the overload set. std::string_view Name; + /// The parent symbol ID. SymbolID Parent; + /// The namespace of the overload set. std::span Namespace; + /// The members of the overload set. std::span Members; + /** + * @brief Constructs an OverloadSet. + * + * @param name The name of the overload set. + * @param parent The parent symbol ID. + * @param ns The namespace of the overload set. + * @param members The members of the overload set. + */ OverloadSet( std::string_view name, const SymbolID& parent, @@ -56,6 +85,14 @@ visit( std::forward(args)...); } +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + OverloadSet const& overloads, + DomCorpus const* domCorpus); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Record.hpp b/include/mrdocs/Metadata/Record.hpp index af8173f01..c1a9d4ddf 100644 --- a/include/mrdocs/Metadata/Record.hpp +++ b/include/mrdocs/Metadata/Record.hpp @@ -47,6 +47,14 @@ struct BaseInfo } }; +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + BaseInfo const& I, + DomCorpus const* domCorpus); + enum class RecordKeyKind { Struct, @@ -56,6 +64,16 @@ enum class RecordKeyKind MRDOCS_DECL dom::String toString(RecordKeyKind kind) noexcept; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordKeyKind kind) +{ + v = toString(kind); +} + /** Metadata for struct, class, or union. */ struct RecordInfo @@ -92,6 +110,24 @@ struct RecordInfo } }; +constexpr +std::string_view +getDefaultAccessString( + RecordKeyKind const& kind) noexcept +{ + switch(kind) + { + case RecordKeyKind::Class: + return "private"; + case RecordKeyKind::Struct: + case RecordKeyKind::Union: + return "public"; + default: + MRDOCS_UNREACHABLE(); + } +} + + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Scope.hpp b/include/mrdocs/Metadata/Scope.hpp index 0f747aa82..3296559b3 100644 --- a/include/mrdocs/Metadata/Scope.hpp +++ b/include/mrdocs/Metadata/Scope.hpp @@ -29,19 +29,59 @@ namespace mrdocs { class are the symbols and functions declared in the class. + The Lookups are the symbols that are accessible + from the scope of the Info. For instance, the + Lookups["foo"] of a namespace are the symbols + declared as "foo" in the namespace. + + This Info class can be used as a base class + for other Info classes, such as NamespaceInfo, + ClassInfo, that represent scopes. This class + can also be used with composition, such as in + @ref Interface to represent different scopes of + the same class (such as member and static overloads). + */ struct ScopeInfo { - /** The members of this scope. - */ - std::vector Members; + /** The members of this scope. + */ + std::vector Members; - /** The lookup table for this scope. - */ - std::unordered_map> Lookups; }; +/** Get a dom::Array of overloads for a scope + + This function takes a ScopeInfo, such as + Namespace or Record, and returns a dom::Array + of overloads in this scope using the DomCorpus + to resolve the SymbolIDs in this scope. + + If the symbol is not overloaded, the + symbol is included in the array. + + When the symbol is overloaded, the OverloadSet + is included in the array. + + This function makes no distinction between + overloads with different access specifiers. + + Instead, we need to traverse the overloads and + generate the data whenever the information + is requested in the handlebars templates + via the LazyObject. + */ +MRDOCS_DECL +dom::Array +generateScopeOverloadsArray( + ScopeInfo const& I, + DomCorpus const& domCorpus); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Source.hpp b/include/mrdocs/Metadata/Source.hpp index 307a9838d..37c397fa3 100644 --- a/include/mrdocs/Metadata/Source.hpp +++ b/include/mrdocs/Metadata/Source.hpp @@ -36,6 +36,16 @@ MRDOCS_DECL std::string_view toString(FileKind kind); +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FileKind kind) +{ + v = toString(kind); +} + struct MRDOCS_DECL Location { @@ -76,6 +86,12 @@ struct MRDOCS_DECL } }; +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Location const& loc); + struct LocationEmptyPredicate { constexpr bool operator()( @@ -113,6 +129,12 @@ struct MRDOCS_DECL SourceInfo() = default; }; +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SourceInfo const& I); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Specifiers.hpp b/include/mrdocs/Metadata/Specifiers.hpp index 919ba09ba..f85640929 100644 --- a/include/mrdocs/Metadata/Specifiers.hpp +++ b/include/mrdocs/Metadata/Specifiers.hpp @@ -165,6 +165,16 @@ enum class OperatorKind Coawait, }; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + OperatorKind kind) +{ + v = static_cast>(kind); +} + /** Reference type kinds */ enum class ReferenceKind @@ -211,10 +221,20 @@ MRDOCS_DECL dom::String toString(StorageClassKind kind) noexcept; MRDOCS_DECL dom::String toString( - const NoexceptInfo& info, + NoexceptInfo const& info, bool resolved = false, bool implicit = false); +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NoexceptInfo const& info) +{ + v = toString(info, false, false); +} + /** Convert ExplicitInfo to a string. @param resolved If true, the operand is not shown when @@ -229,6 +249,66 @@ toString( bool resolved = false, bool implicit = false); +/** Return the ExplicitInfo as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ExplicitInfo const& I) +{ + v = toString(I); +} + +/** Return the AccessKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + AccessKind kind) +{ + v = toString(kind); +} + +/** Return the ConstexprKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ConstexprKind kind) +{ + v = toString(kind); +} + +/** Return the StorageClassKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + StorageClassKind kind) +{ + v = toString(kind); +} + +/** Return the ReferenceKind as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ReferenceKind kind) +{ + v = toString(kind); +} + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Symbols.hpp b/include/mrdocs/Metadata/Symbols.hpp index 8000f490e..d711d9dc1 100644 --- a/include/mrdocs/Metadata/Symbols.hpp +++ b/include/mrdocs/Metadata/Symbols.hpp @@ -19,6 +19,15 @@ #include #include #include +#include + +namespace clang::mrdocs { + class DomCorpus; + namespace dom { + struct ValueFromTag; + class Value; + } +} namespace clang { namespace mrdocs { @@ -151,6 +160,35 @@ compareSymbolNames( std::string_view symbolName0, std::string_view symbolName1) noexcept; +/** Convert SymbolID to dom::Value string in the DOM using toBase16 + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SymbolID const& id); + +/** Convert SymbolID to dom::Value object in the DOM using Corpus + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SymbolID const& id, + DomCorpus const* domCorpus); + +/** Convert SymbolID pointers to dom::Value or null. + */ +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& t, + DomCorpus const* domCorpus); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Template.hpp b/include/mrdocs/Metadata/Template.hpp index 74e628b5b..dddb90266 100644 --- a/include/mrdocs/Metadata/Template.hpp +++ b/include/mrdocs/Metadata/Template.hpp @@ -36,6 +36,16 @@ enum class TArgKind : int MRDOCS_DECL std::string_view toString(TArgKind kind) noexcept; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TArgKind kind) +{ + v = toString(kind); +} + struct TArg { /** The kind of template argument this is. */ @@ -133,6 +143,30 @@ visit( MRDOCS_DECL std::string toString(const TArg& arg) noexcept; +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TArg const& I, + DomCorpus const* domCorpus); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + // ---------------------------------------------------------------- enum class TParamKind : int @@ -176,6 +210,30 @@ struct TParam } }; +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TParam const& I, + DomCorpus const* domCorpus); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + + template struct IsTParam : TParam { @@ -202,6 +260,16 @@ enum class TParamKeyKind : int MRDOCS_DECL std::string_view toString(TParamKeyKind kind) noexcept; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TParamKeyKind kind) +{ + v = toString(kind); +} + struct TypeTParam : IsTParam { @@ -307,6 +375,30 @@ struct TemplateInfo } }; +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TemplateInfo const& I, + DomCorpus const* domCorpus); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Type.hpp b/include/mrdocs/Metadata/Type.hpp index b73c4ef2e..bb3941d6b 100644 --- a/include/mrdocs/Metadata/Type.hpp +++ b/include/mrdocs/Metadata/Type.hpp @@ -35,6 +35,16 @@ enum QualifierKind MRDOCS_DECL dom::String toString(QualifierKind kind) noexcept; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + QualifierKind kind) +{ + v = toString(kind); +} + enum class TypeKind { Named = 1, // for bitstream @@ -50,6 +60,16 @@ enum class TypeKind MRDOCS_DECL dom::String toString(TypeKind kind) noexcept; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TypeKind kind) +{ + v = toString(kind); +} + enum class AutoKind { Auto, @@ -58,6 +78,16 @@ enum class AutoKind MRDOCS_DECL dom::String toString(AutoKind kind) noexcept; +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + AutoKind kind) +{ + v = toString(kind); +} + struct TypeInfo { /** The kind of TypeInfo this is @@ -103,6 +133,30 @@ struct TypeInfo } }; +MRDOCS_DECL +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TypeInfo const& I, + DomCorpus const* domCorpus); + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& I, + DomCorpus const* domCorpus) +{ + if (!I) + { + v = nullptr; + return; + } + tag_invoke(dom::ValueFromTag{}, v, *I, domCorpus); +} + template struct IsType : TypeInfo { @@ -285,6 +339,7 @@ toString( const TypeInfo& T, std::string_view Name = ""); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Using.hpp b/include/mrdocs/Metadata/Using.hpp index ae53e0b0c..a21d0e88c 100644 --- a/include/mrdocs/Metadata/Using.hpp +++ b/include/mrdocs/Metadata/Using.hpp @@ -41,6 +41,19 @@ toString(UsingClass const& value) return "unknown"; } +/** Return the UsingClass as a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + UsingClass kind) +{ + v = toString(kind); +} + + /** Info for using declarations. */ struct UsingInfo diff --git a/src/lib/Dom/LazyArray.hpp b/src/lib/Dom/LazyArray.hpp new file mode 100644 index 000000000..261ed6189 --- /dev/null +++ b/src/lib/Dom/LazyArray.hpp @@ -0,0 +1,187 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_DOM_LAZY_ARRAY_HPP +#define MRDOCS_LIB_DOM_LAZY_ARRAY_HPP + +#include "mrdocs/Dom.hpp" +#include "mrdocs/Platform.hpp" +#include "mrdocs/Support/Error.hpp" +#include +#include + +namespace clang { +namespace mrdocs { +namespace dom { + +namespace detail { + struct no_context_tag {}; + struct no_size_tag {}; + + template + concept IsCallableContext = + std::invocable> && + HasStandaloneValueFrom>>; +} + +/** Lazy array implementation + + This array type is used to define a dom::Array + whose members are evaluated on demand + as they are accessed. + + Each member can goes through a transform + function before being returned as a Value so + that all types can be converted to dom::Value. + + The underlying representation of the array is + a range from where the elements are extracted. + Elements in this range should be convertible + to dom::Value. + + This class is typically useful for + implementing arrays that are expensive + and have recursive dependencies, as these + recursive dependencies can also be deferred. + + Unlike a LazyObjectImpl, which contains an + overlay object, this implementation is + read-only. The `set` and `emplace_back` + methods are not implemented. + +*/ +template +requires + HasValueFrom, Context> || + (std::invocable> && + HasStandaloneValueFrom>>) +class LazyArrayImpl : public ArrayImpl +{ + using const_iterator_t = decltype(std::ranges::cbegin(std::declval())); + using const_sentinel_t = decltype(std::ranges::cend(std::declval())); + using size_type = std::conditional_t< + std::ranges::sized_range, + std::ranges::range_size_t, + detail::no_size_tag>; + + + + const_iterator_t begin_; + const_sentinel_t end_; + [[no_unique_address]] size_type size_; + [[no_unique_address]] Context context_; + +public: + explicit + LazyArrayImpl(R const& arr) + : begin_(std::ranges::begin(arr)) + , end_(std::ranges::end(arr)) + { + if constexpr (std::ranges::sized_range) + { + size_ = std::ranges::size(arr); + } + } + + explicit + LazyArrayImpl(R const& arr, Context const& ctx) + : begin_(std::ranges::begin(arr)) + , end_(std::ranges::end(arr)) + , context_(std::move(ctx)) + { + if constexpr (std::ranges::sized_range) + { + size_ = std::ranges::size(arr); + } + } + + ~LazyArrayImpl() override = default; + + /// @copydoc ObjectImpl::type_key + char const* + type_key() const noexcept override + { + return "LazyArray"; + } + + std::size_t + size() const noexcept override + { + if constexpr (std::ranges::sized_range) + { + return size_; + } + else + { + return std::ranges::distance(begin_, end_); + } + } + + dom::Value + get(std::size_t i) const override + { + if (i >= size()) + { + return {}; + } + auto it = begin_; + std::ranges::advance(it, i); + if constexpr (std::is_same_v) + { + return ValueFrom(*it); + } + else if constexpr (detail::IsCallableContext) + { + return ValueFrom(context_(*it)); + } + else + { + return ValueFrom(*it, context_); + } + } +}; + +/** Return a new @ref dom::Array based on a lazy array implementation. + */ +template +requires HasStandaloneValueFrom> +Array +LazyArray(T const& arr) +{ + return newArray>(arr); +} + +/** Return a new dom::Array based on a FromValue context +*/ +template +requires HasValueFrom, Context> +Array +LazyArray(T const& arr, Context const& ctx) +{ + return newArray>(arr, ctx); +} + +/** Return a new dom::Array based on a transformed lazy array implementation. +*/ +template +requires + std::invocable> && + HasStandaloneValueFrom>> +Array +TransformArray(T const& arr, F const& f) +{ + return newArray>(arr, f); +} + +} // dom +} // mrdocs +} // clang + +#endif diff --git a/src/lib/Dom/LazyObject.hpp b/src/lib/Dom/LazyObject.hpp index 06dff09f7..d20a0be60 100644 --- a/src/lib/Dom/LazyObject.hpp +++ b/src/lib/Dom/LazyObject.hpp @@ -8,8 +8,8 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_API_DOM_MAPPING_TRAITS_HPP -#define MRDOCS_API_DOM_MAPPING_TRAITS_HPP +#ifndef MRDOCS_LIB_DOM_LAZY_OBJECT_HPP +#define MRDOCS_LIB_DOM_LAZY_OBJECT_HPP #include "mrdocs/Dom.hpp" #include "mrdocs/Platform.hpp" @@ -20,32 +20,9 @@ namespace clang { namespace mrdocs { 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 { - template - 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 -struct MappingTraits { - // void map(dom::IO &io, T &fields) const; -}; - namespace detail { - /** A class representing an archetypal IO object. + /* A class representing an archetypal IO object. */ struct MRDOCS_DECL ArchetypalIO { @@ -57,67 +34,152 @@ namespace detail void defer(std::string_view, F const&) const {} }; + + /* A helper empty struct + */ + struct NoLazyObjectContext { }; } -/// Concept to determine if @ref MappingTraits is defined for a type T -template -concept HasMappingTraits = requires(detail::ArchetypalIO& io, T const& o) +/** Customization point tag. + + This tag type is used by the class + @ref dom::LazyObjectImpl to select overloads + of `tag_invoke`. + + @note This type is empty; it has no members. + + @see @ref dom::LazyObjectImpl + + tag_invoke: A general pattern for supporting customisable functions +*/ +struct LazyObjectMapTag { }; + +/** Concept to determine if a type can be mapped to a + @ref dom::LazyObjectImpl with a user-provided conversion. + + This concept determines if the user-provided conversion is + defined as: + + @code + template + void tag_invoke( LazyObjectMapTag, IO&, T& ); + @endcode + + This customization can be defined by any type that needs to be converted + to/from a lazy @ref dom::Object. For example: + + @code + template + void tag_invoke( LazyObjectMapTag, IO& io, MyStruct const& s) + { + io.map("name", s.name); + io.map("size", s.size); + io.map("age", s.age); + } + @endcode + + */ +template +concept HasLazyObjectMapWithoutContext = requires( + detail::ArchetypalIO& io, + T const& t) +{ + { tag_invoke(LazyObjectMapTag{}, io, t) } -> std::same_as; +}; + +/** Concept to determine if a type can be mapped to a + @ref dom::LazyObjectImpl with a user-provided conversion. + + This concept determines if the user-provided conversion is + defined as: + + @code + template + void tag_invoke( LazyObjectMapTag, IO&, T, Context const& ); + @endcode + */ +template +concept HasLazyObjectMapWithContext = requires( + detail::ArchetypalIO& io, + T const& t, + Context const& ctx) { - { std::declval>().map(io, o) } -> std::same_as; + { tag_invoke(LazyObjectMapTag{}, io, t, ctx) } -> std::same_as; }; +/** Determine if `T` can be converted to @ref dom::Value. + + If `T` can be converted to @ref dom::Value via a + call to @ref dom::ValueFrom, the static data member `value` + is defined as `true`. Otherwise, `value` is + defined as `false`. +*/ +template +concept HasLazyObjectMap = + HasLazyObjectMapWithContext || + HasLazyObjectMapWithoutContext; + //------------------------------------------------ // // LazyObjectImpl // //------------------------------------------------ -/** Abstract lazy object interface. +/** Lazy object implementation. This interface is used to define objects whose members are evaluated on demand as they are accessed. - 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, + the object @ref dom::Value is constructed. + In practice, the object never takes any memory + besides the pointer to the underlying 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. + The keys and values in the underlying object + should be mapped using `tag_invoke`. This class is typically useful for implementing objects that are expensive and have recursive dependencies, as these recursive dependencies can also be deferred. + A context can also be stored in the object + as a form to customize how the object is + mapped. This context should be copyable + and is propagated to other objects that + support an overload with the same context. + + The context can be simply a tag + identifying how to map the object, or + a more complex object carrying data + to customize the mapping process. + + In the latter case, because the context + should be a copyable, the user might want + to use a type with reference semantics. + */ -template +template +requires HasLazyObjectMap class LazyObjectImpl : public ObjectImpl { T const* underlying_; Object overlay_; - [[no_unique_address]] MappingTraits traits_{}; + [[no_unique_address]] Context context_{}; public: explicit LazyObjectImpl(T const& obj) - requires std::constructible_from> + requires HasLazyObjectMapWithoutContext : underlying_(&obj) - , traits_{} {} + , context_{} {} explicit - LazyObjectImpl(T const& obj, MappingTraits traits) + LazyObjectImpl(T const& obj, Context const& context) + requires HasLazyObjectMapWithContext : underlying_(&obj) - , traits_(std::move(traits)) {} + , context_(context) {} ~LazyObjectImpl() override = default; @@ -151,216 +213,238 @@ class LazyObjectImpl : public ObjectImpl namespace detail { - class GetterIO + /* The IO object for lazy objects. + + Mapping traits use this object to call the + map and defer methods, which are used to + access properties of the lazy object. + + Each function provides different behavior + to `map` and `defer` methods, allowing + to implement functionality such as + `get`, `set`, `visit`, and `size`. + + In some cases, only a function for + `map` is provided, and the `defer` function + is not used. In this case, the `defer` function + also uses the `map` function, which is + the default behavior. + + In other cases, the `defer` function is + used to defer the evaluation of a property + to a later time, which is useful for functionality + that requires accessing the value. + */ + template + class LazyObjectIO { - std::string_view key; - Value result; + MapFn mapFn; + DeferFn deferFn; public: explicit - GetterIO(std::string_view key) - : key(key) {} + LazyObjectIO(MapFn mapFn, DeferFn deferFn = {}) + : mapFn(mapFn), deferFn(deferFn) {} template - requires std::constructible_from void map(std::string_view name, T const& value) { - if (result.isUndefined() && name == key) - { - this->result = Value(value); - } + mapFn(name, value); } - template + template void - defer(std::string_view name, T const& deferred) + defer(std::string_view name, F&& deferred) { - using R = std::invoke_result_t; - if constexpr (std::constructible_from) + if constexpr (std::same_as) { - if (result.isUndefined() && name == key) - { - this->result = deferred(); - } + mapFn(name, deferred); + } + else + { + deferFn(name, deferred); } - } - - Value - get() - { - return std::move(result); } }; } -template -Value -LazyObjectImpl:: -get(std::string_view key) const +template +requires HasLazyObjectMap +std::size_t +LazyObjectImpl:: +size() const { - if (overlay_.exists(key)) + std::size_t result; + detail::LazyObjectIO io( + [&result, this](std::string_view name, auto const& /* value or deferred */) + { + result += !overlay_.exists(name); + }); + if constexpr (HasLazyObjectMapWithContext) { - return overlay_.get(key); + tag_invoke(LazyObjectMapTag{}, io, *underlying_, context_); } - detail::GetterIO io{key}; - traits_.map(io, *underlying_); - return io.get(); + else + { + tag_invoke(LazyObjectMapTag{}, io, *underlying_); + } + return result + overlay_.size(); } -template -void -LazyObjectImpl:: -set(String key, Value value) +template +requires HasLazyObjectMap +bool +LazyObjectImpl:: +exists(std::string_view key) const { - overlay_.set(std::move(key), std::move(value)); + if (overlay_.exists(key)) + { + return true; + } + bool result = false; + detail::LazyObjectIO io( + [&result, key](std::string_view name, auto const& /* value or deferred */) + { + if (!result && name == key) + { + result = true; + } + }); + if constexpr (HasLazyObjectMapWithContext) + { + tag_invoke(LazyObjectMapTag{}, io, *underlying_, context_); + } + else + { + tag_invoke(LazyObjectMapTag{}, io, *underlying_); + } + return result; } -namespace detail + +template +requires HasLazyObjectMap +Value +LazyObjectImpl:: +get(std::string_view key) const { - class VisitIO + if (overlay_.exists(key)) { - std::function fn; - Object const& overlay; - bool continueVisiting = true; - public: - explicit - VisitIO(std::function fn, Object const& overlay) - : fn(fn) - , overlay(overlay) {} - - template - void - map(std::string_view name, T const& value) + return overlay_.get(key); + } + Value result; + detail::LazyObjectIO io( + [&result, key, this](std::string_view name, auto const& value) { - if (continueVisiting && !overlay.exists(name)) + if (result.isUndefined() && name == key) { - continueVisiting = fn(name, Value(value)); + if constexpr (HasValueFromWithContext, Context>) + { + ValueFrom(value, context_, result); + } + else + { + ValueFrom(value, result); + } } - } - - template - void - defer(std::string_view name, T const& deferred) + }, [&result, key, this](std::string_view name, auto const& deferred) { - if (continueVisiting && !overlay.exists(name)) + if (result.isUndefined() && name == key) { - continueVisiting = fn(name, deferred()); + if constexpr (HasValueFromWithContext, Context>) + { + ValueFrom(deferred(), context_, result); + } + else + { + ValueFrom(deferred(), result); + } } - } - - bool - get() - { - return continueVisiting; - } - }; -} - -template -bool -LazyObjectImpl:: -visit(std::function fn) const -{ - detail::VisitIO io{fn, overlay_}; - traits_.map(io, *underlying_); - return io.get() && overlay_.visit(fn); -} - -namespace detail -{ - class SizeIO + }); + if constexpr (HasLazyObjectMapWithContext) { - Object const& overlay; - std::size_t result = 0; - public: - explicit - SizeIO(Object const& overlay) - : overlay(overlay) {} - - template - void - map(std::string_view name, T const&) - { - this->result += !overlay.exists(name); - } - - template - void - defer(std::string_view name, T const&) - { - this->result += !overlay.exists(name); - } - - std::size_t - get() - { - return result + overlay.size(); - } - }; + tag_invoke(LazyObjectMapTag{}, io, *underlying_, context_); + } + else + { + tag_invoke(LazyObjectMapTag{}, io, *underlying_); + } + return result; } -template -std::size_t -LazyObjectImpl:: -size() const +template +requires HasLazyObjectMap +void +LazyObjectImpl:: +set(String key, Value value) { - detail::SizeIO io{overlay_}; - traits_.map(io, *underlying_); - return io.get(); + overlay_.set(std::move(key), std::move(value)); } -namespace detail +template +requires HasLazyObjectMap +bool +LazyObjectImpl:: +visit(std::function fn) const { - class ExistsIO - { - std::string_view key; - bool result = false; - public: - explicit - ExistsIO(std::string_view key) - : key(key) {} - - template - void - map(std::string_view name, T const&) + bool visitMore = true; + detail::LazyObjectIO io( + [&visitMore, &fn, this](std::string_view name, auto const& value) { - if (!result && name == key) + if (visitMore && !overlay_.exists(name)) { - this->result = true; + if constexpr (HasValueFromWithContext, Context>) + { + visitMore = fn(name, dom::ValueFrom(value, context_)); + } + else + { + visitMore = fn(name, dom::ValueFrom(value)); + } } - } - - template - void - defer(std::string_view name, T const&) + }, [&visitMore, &fn, this](std::string_view name, auto const& deferred) { - if (!result && name == key) + if (visitMore && !overlay_.exists(name)) { - this->result = true; + if constexpr (HasValueFromWithContext, Context>) + { + visitMore = fn(name, dom::ValueFrom(deferred(), context_)); + } + else + { + visitMore = fn(name, dom::ValueFrom(deferred())); + } } - } - - bool - get() - { - return result; - } - }; + }); + if constexpr (HasLazyObjectMapWithContext) + { + tag_invoke(LazyObjectMapTag{}, io, *underlying_, context_); + } + else + { + tag_invoke(LazyObjectMapTag{}, io, *underlying_); + } + return visitMore && overlay_.visit(fn); } -template -bool -LazyObjectImpl:: -exists(std::string_view key) const +/** Return a new dom::Object based on a lazy object implementation. +*/ +template +Object +LazyObject(T const& obj) { - if (overlay_.exists(key)) - { - return true; - } - detail::ExistsIO io{key}; - traits_.map(io, *underlying_); - return io.get(); + return newObject>(obj); +} + +/** Return a new dom::Object based on a transformed lazy array implementation. +*/ +template +requires HasLazyObjectMap +Object +LazyObject(T const& arr, Context const& context) +{ + return newObject>(arr, context); } } // dom diff --git a/src/lib/Metadata/DomCorpus.cpp b/src/lib/Metadata/DomCorpus.cpp index d58b134f5..57057c9c9 100644 --- a/src/lib/Metadata/DomCorpus.cpp +++ b/src/lib/Metadata/DomCorpus.cpp @@ -5,6 +5,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -12,6 +13,7 @@ #include "lib/Support/Radix.hpp" #include "lib/Support/LegibleNames.hpp" #include "lib/Dom/LazyObject.hpp" +#include "lib/Dom/LazyArray.hpp" #include #include #include @@ -21,619 +23,6 @@ namespace clang { namespace mrdocs { -namespace { - -//------------------------------------------------ -// -// Helpers -// -//------------------------------------------------ - -dom::Value -domCreate( - std::unique_ptr const& jd, - DomCorpus const& domCorpus) -{ - if(!jd) - return nullptr; - return domCorpus.getJavadoc(*jd); -} - -/* A Lazy DOM Array type that replaces symbol IDs with their - corresponding DOM objects. -*/ -class DomSymbolArray : public dom::ArrayImpl -{ - std::span list_; - DomCorpus const& domCorpus_; - -public: - DomSymbolArray( - std::span list, - DomCorpus const& domCorpus) noexcept - : list_(list) - , domCorpus_(domCorpus) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - if (i < list_.size()) - { - return domCorpus_.get(list_[i]); - } - return dom::Value{}; - } -}; - -//------------------------------------------------ - -dom::Object -domCreate( - OverloadSet const& overloads, - DomCorpus const& domCorpus) -{ - return dom::Object({ - { "kind", "overload"}, - { "name", overloads.Name }, - { "members", dom::newArray( - overloads.Members, domCorpus) }, - { "namespace", dom::newArray( - overloads.Namespace, domCorpus) }, - { "parent", domCorpus.get(overloads.Parent) } - }); -} - -class DomOverloadsArray : public dom::ArrayImpl -{ - std::vector> overloads_; - - DomCorpus const& domCorpus_; - -public: - template ScopeInfoTy> - DomOverloadsArray( - const ScopeInfoTy& I, - DomCorpus const& domCorpus) noexcept - : domCorpus_(domCorpus) - { - overloads_.reserve(I.Lookups.size()); - domCorpus_->traverseOverloads(I, - [&](const auto& C) - { - if constexpr(requires { C.id; }) - overloads_.emplace_back(C.id); - else - overloads_.emplace_back(C); - }); - } - - std::size_t size() const noexcept override - { - return overloads_.size(); - } - - dom::Value get(std::size_t index) const override - { - MRDOCS_ASSERT(index < size()); - const auto& member = overloads_[index]; - if(auto* id = std::get_if(&member)) - return domCorpus_.get(*id); - return domCorpus_.getOverloads( - std::get(member)); - } -}; - -//------------------------------------------------ -// -// Location -// SourceInfo -// -//------------------------------------------------ - -dom::Object -domCreate(Location const& loc) -{ - return dom::Object({ - { "path", loc.Path }, - { "file", loc.Filename }, - { "line", loc.LineNumber }, - { "kind", toString(loc.Kind) }, - { "documented", loc.Documented } - }); -} - -class DomLocationArray : public dom::ArrayImpl -{ - std::vector const& list_; - -public: - explicit - DomLocationArray( - std::vector const& list) noexcept - : list_(list) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - MRDOCS_ASSERT(i < list_.size()); - return domCreate(list_[i]); - } -}; - -dom::Object -domCreate(SourceInfo const& I) -{ - dom::Object::storage_type entries; - if(I.DefLoc) - entries.emplace_back("def", domCreate(*I.DefLoc)); - if(! I.Loc.empty()) - entries.emplace_back("decl", dom::newArray(I.Loc)); - return dom::Object(std::move(entries)); -} - -//------------------------------------------------ -// -// TypeInfo -// -//------------------------------------------------ - -static dom::Value domCreate( - std::unique_ptr const&, DomCorpus const&); - -class DomTypeInfoArray : public dom::ArrayImpl -{ - std::vector> const& list_; - DomCorpus const& domCorpus_; - -public: - DomTypeInfoArray( - std::vector> const& list, - DomCorpus const& domCorpus) noexcept - : list_(list) - , domCorpus_(domCorpus) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - MRDOCS_ASSERT(i < list_.size()); - return domCreate(list_[i], domCorpus_); - } -}; - -//------------------------------------------------ -// -// Param -// -//------------------------------------------------ - -/** An array of function parameters -*/ -class DomParamArray : public dom::ArrayImpl -{ - std::vector const& list_; - DomCorpus const& domCorpus_; - -public: - DomParamArray( - std::vector const& list, - DomCorpus const& domCorpus) noexcept - : list_(list) - , domCorpus_(domCorpus) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - MRDOCS_ASSERT(i < list_.size()); - auto const& I = list_[i]; - return dom::Object({ - { "name", dom::stringOrNull(I.Name) }, - { "type", domCreate(I.Type, domCorpus_) }, - { "default", dom::stringOrNull(I.Default) } - }); - } -}; - -//------------------------------------------------ -// -// TemplateInfo -// -//------------------------------------------------ - -static dom::Value domCreate( - std::unique_ptr const&, DomCorpus const&); -static dom::Value domCreate( - std::unique_ptr const&, DomCorpus const&); -static dom::Value domCreate( - std::unique_ptr const& I, DomCorpus const&); -static dom::Value domCreate( - std::unique_ptr const& I, DomCorpus const&); - -//------------------------------------------------ - -/** An array of template arguments -*/ -class DomTArgArray : public dom::ArrayImpl -{ - std::vector> const& list_; - DomCorpus const& domCorpus_; - -public: - DomTArgArray( - std::vector> const& list, - DomCorpus const& domCorpus) noexcept - : list_(list) - , domCorpus_(domCorpus) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - MRDOCS_ASSERT(i < list_.size()); - return domCreate(list_[i], domCorpus_); - } -}; - -/** An array of template parameters -*/ -class DomTParamArray : public dom::ArrayImpl -{ - std::vector> const& list_; - DomCorpus const& domCorpus_; - -public: - DomTParamArray( - std::vector> const& list, - DomCorpus const& domCorpus) noexcept - : list_(list) - , domCorpus_(domCorpus) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - return domCreate(list_[i], domCorpus_); - } -}; - -//------------------------------------------------ - -static -dom::Value -domCreate( - std::unique_ptr const& I, - DomCorpus const& domCorpus) -{ - if(! I) - return nullptr; - dom::Object::storage_type entries = { - { "kind", toString(I->Kind) }, - { "is-pack", I->IsPackExpansion } - }; - visit(*I, [&](const T& t) - { - if constexpr(T::isType()) - { - entries.emplace_back("type", - domCreate(t.Type, domCorpus)); - } - if constexpr(T::isNonType()) - { - entries.emplace_back("value", - t.Value.Written); - } - if constexpr(T::isTemplate()) - { - entries.emplace_back("name", - t.Name); - entries.emplace_back("template", - domCorpus.get(t.Template)); - } - }); - return dom::Object(std::move(entries)); -} - -static -dom::Value -domCreate( - std::unique_ptr const& I, - DomCorpus const& domCorpus) -{ - if(! I) - return nullptr; - dom::Object::storage_type entries = { - { "kind", toString(I->Kind) }, - { "name", dom::stringOrNull(I->Name) }, - { "is-pack", I->IsParameterPack } - }; - visit(*I, [&](const T& t) - { - if(t.Default) - entries.emplace_back("default", - domCreate(t.Default, domCorpus)); - - if constexpr(T::isType()) - { - entries.emplace_back("key", - toString(t.KeyKind)); - if(t.Constraint) - entries.emplace_back("constraint", - domCreate(t.Constraint, domCorpus)); - } - if constexpr(T::isNonType()) - { - entries.emplace_back("type", - domCreate(t.Type, domCorpus)); - } - if constexpr(T::isTemplate()) - { - entries.emplace_back("params", - dom::newArray( - t.Params, domCorpus)); - } - }); - return dom::Object(std::move(entries)); -} - -static -dom::Value -domCreate( - std::unique_ptr const& I, - DomCorpus const& domCorpus) -{ - if(! I) - return nullptr; - return dom::Object({ - { "kind", toString(I->specializationKind()) }, - { "primary", domCorpus.get(I->Primary) }, - { "params", dom::newArray( I->Params, domCorpus) }, - { "args", dom::newArray(I->Args, domCorpus) }, - { "requires", dom::stringOrNull(I->Requires.Written) } - }); -} - -//------------------------------------------------ - -static -dom::Value -domCreate( - std::unique_ptr const& I, - DomCorpus const& domCorpus) -{ - if(! I) - return nullptr; - dom::Object::storage_type entries = { - { "kind", toString(I->Kind) } - }; - visit(*I, [&](const T& t) - { - entries.emplace_back("name", t.Name); - entries.emplace_back("symbol", domCorpus.get(t.id)); - - if constexpr(requires { t.TemplateArgs; }) - entries.emplace_back("args", - dom::newArray(t.TemplateArgs, domCorpus)); - - entries.emplace_back("prefix", domCreate(t.Prefix, domCorpus)); - }); - return dom::Object(std::move(entries)); -} - -static -dom::Value -domCreate( - std::unique_ptr const& I, - DomCorpus const& domCorpus) -{ - if(! I) - return nullptr; - dom::Object::storage_type entries = { - { "kind", toString(I->Kind) }, - { "is-pack", I->IsPackExpansion }, - }; - visit(*I, [&](const T& t) - { - if constexpr(T::isNamed()) - entries.emplace_back("name", - domCreate(t.Name, domCorpus)); - - if constexpr(T::isDecltype()) - entries.emplace_back("operand", t.Operand.Written); - - if constexpr(T::isAuto()) - { - entries.emplace_back("keyword", - toString(t.Keyword)); - if(t.Constraint) - entries.emplace_back("constraint", - domCreate(t.Constraint, domCorpus)); - } - - if constexpr(requires { t.CVQualifiers; }) - entries.emplace_back("cv-qualifiers", - toString(t.CVQualifiers)); - - if constexpr(requires { t.ParentType; }) - entries.emplace_back("parent-type", - domCreate(t.ParentType, domCorpus)); - - if constexpr(requires { t.PointeeType; }) - entries.emplace_back("pointee-type", - domCreate(t.PointeeType, domCorpus)); - - if constexpr(T::isArray()) - { - entries.emplace_back("element-type", - domCreate(t.ElementType, domCorpus)); - if(t.Bounds.Value) - entries.emplace_back("bounds-value", - *t.Bounds.Value); - entries.emplace_back("bounds-expr", - t.Bounds.Written); - } - - if constexpr(T::isFunction()) - { - entries.emplace_back("return-type", - domCreate(t.ReturnType, domCorpus)); - entries.emplace_back("param-types", - dom::newArray(t.ParamTypes, domCorpus)); - entries.emplace_back("exception-spec", - toString(t.ExceptionSpec)); - entries.emplace_back("ref-qualifier", - toString(t.RefQualifier)); - entries.emplace_back("is-variadic", t.IsVariadic); - } - }); - return dom::Object(std::move(entries)); -} - -//------------------------------------------------ -// -// BaseInfo -// -//------------------------------------------------ - -class DomBaseArray : public dom::ArrayImpl -{ - std::vector const& list_; - DomCorpus const& domCorpus_; - -public: - DomBaseArray( - std::vector const& list, - DomCorpus const& domCorpus) noexcept - : list_(list) - , domCorpus_(domCorpus) - { - } - - std::size_t size() const noexcept override - { - return list_.size(); - } - - dom::Value get(std::size_t i) const override - { - auto const& I = list_[i]; - return dom::Object({ - { "access", toString(I.Access) }, - { "isVirtual", I.IsVirtual }, - { "type", domCreate(I.Type, domCorpus_) } - }); - } -}; - -//------------------------------------------------ -// -// Interface -// -//------------------------------------------------ - -class DomTranche : public dom::DefaultObjectImpl -{ - std::shared_ptr tranche_; - DomCorpus const& domCorpus_; - - static - dom::Value - init( - std::span list, - DomCorpus const& domCorpus) - { - return dom::newArray(list, domCorpus); - } - - static - dom::Value - init( - const ScopeInfo& scope, - DomCorpus const& domCorpus) - { - return dom::newArray(scope, domCorpus); - } - -public: - DomTranche( - std::shared_ptr const& tranche, - DomCorpus const& domCorpus) noexcept - : dom::DefaultObjectImpl({ - #define INFO(Plural, LC_Plural) \ - { #LC_Plural, init(tranche->Plural, domCorpus) }, - #include - { "types", init(tranche->Types, domCorpus) }, - { "staticfuncs", init(tranche->StaticFunctions, domCorpus) }, - { "overloads", init(tranche->Overloads, domCorpus) }, - { "staticoverloads", init(tranche->StaticOverloads, domCorpus) }, - }) - , tranche_(tranche) - , domCorpus_(domCorpus) - { - } -}; - -//------------------------------------------------ -// -// Info -// -//------------------------------------------------ - -std::string_view -getDefaultAccess( - RecordInfo const& I) noexcept -{ - switch(I.KeyKind) - { - case RecordKeyKind::Class: - return "private"; - case RecordKeyKind::Struct: - case RecordKeyKind::Union: - return "public"; - default: - MRDOCS_UNREACHABLE(); - } -} - -//------------------------------------------------ - -} // (anon) - -//------------------------------------------------ - class DomCorpus::Impl { using value_type = std::weak_ptr; @@ -707,292 +96,12 @@ getCorpus() const return impl_->getCorpus(); } -Corpus const& -DomCorpus:: -operator*() const -{ - return getCorpus(); -} - -Corpus const* -DomCorpus:: -operator->() const -{ - return &getCorpus(); -} - -namespace dom { - /* Determine if a type has a mrdocs::toString overload - */ - template - concept HasMrDocsToString = requires(U u) - { - { mrdocs::toString(u) } -> std::convertible_to; - }; - - /* Convert enum Values to strings using mrdocs::toString - */ - template - requires std::is_enum_v - struct ToValue - { - std::string_view - operator()(U const& v) const - { - return mrdocs::toString(v); - } - }; - - static_assert(HasToValue); - static_assert(HasToValue); - - /* Convert SymbolID to strings using toBase16 - */ - template <> - struct ToValue - { - std::string - operator()(SymbolID const& id) const - { - return toBase16(id); - } - }; - - static_assert(HasToValue); - - /* Mapping Traits for an Info type - - These traits map an Info type to a DOM object. - It includes all members of the derived type. - - The traits store a reference to the DomCorpus - so that it can resolve symbol IDs to the corresponding - Info objects. Whenever a member refers to symbol IDs, - the mapping trait will automatically resolve the - symbol ID to the corresponding Info object. - - This allows all references to be resolved to the - corresponding Info object lazily from the templates - that use the DOM. - */ - template T> - struct MappingTraits - { - private: - DomCorpus const* domCorpus_ = nullptr; - - public: - MappingTraits(DomCorpus const& domCorpus) - : domCorpus_(&domCorpus) - { - } - - template - void - map(IO &io, T const& I) const - { - MRDOCS_ASSERT(domCorpus_); - io.map("id", I.id); - if (!I.Name.empty()) - { - io.map("name", I.Name); - } - io.map("kind", I.Kind); - io.map("access", I.Access); - io.map("implicit", I.Implicit); - io.defer("namespace", [&]{ return dom::newArray(I.Namespace, *domCorpus_); }); - io.defer("doc", [&]{ return domCreate(I.javadoc, *domCorpus_); }); - if (!I.Namespace.empty()) - { - io.defer("parent", [&]{ return domCorpus_->get(I.Namespace.front()); }); - } - if constexpr(std::derived_from) - { - io.defer("members", [&]{ return dom::newArray(I.Members, *domCorpus_); }); - io.defer("overloads", [&]{ return dom::newArray(I, *domCorpus_); }); - } - if constexpr(std::derived_from) - { - io.defer("loc", [&]{ return domCreate(I); }); - } - if constexpr(T::isNamespace()) - { - io.defer("interface", [&]{ return dom::newObject( - std::make_shared( - makeTranche(I, **domCorpus_)), - *domCorpus_); }); - io.defer("usingDirectives", [&]{ return dom::newArray( - I.UsingDirectives, *domCorpus_); }); - } - if constexpr (T::isRecord()) - { - io.map("tag", I.KeyKind); - io.defer("defaultAccess", [&]{ return getDefaultAccess(I); }); - io.map("isTypedef", I.IsTypeDef); - io.defer("bases", [&]{ return dom::newArray(I.Bases, *domCorpus_); }); - io.defer("interface", [&]{ - auto sp = std::make_shared(makeInterface(I, domCorpus_->getCorpus())); - return dom::Object({ - { "public", dom::newObject(sp->Public, *domCorpus_) }, - { "protected", dom::newObject(sp->Protected, *domCorpus_) }, - { "private", dom::newObject(sp->Private, *domCorpus_) }, - // { "overloads", dom::newArray(sp->Overloads, *domCorpus_) }, - // { "static-overloads", dom::newArray(sp->StaticOverloads, *domCorpus_) } - }); - }); - io.defer("template", [&]{ return domCreate(I.Template, *domCorpus_); }); - } - if constexpr (T::isEnum()) - { - io.defer("type", [&]{ return domCreate(I.UnderlyingType, *domCorpus_); }); - io.map("isScoped", I.Scoped); - } - if constexpr (T::isFunction()) - { - io.map("isVariadic", I.IsVariadic); - io.map("isVirtual", I.IsVirtual); - io.map("isVirtualAsWritten", I.IsVirtualAsWritten); - io.map("isPure", I.IsPure); - io.map("isDefaulted", I.IsDefaulted); - io.map("isExplicitlyDefaulted", I.IsExplicitlyDefaulted); - io.map("isDeleted", I.IsDeleted); - io.map("isDeletedAsWritten", I.IsDeletedAsWritten); - io.map("isNoReturn", I.IsNoReturn); - io.map("hasOverrideAttr", I.HasOverrideAttr); - io.map("hasTrailingReturn", I.HasTrailingReturn); - io.map("isConst", I.IsConst); - io.map("isVolatile", I.IsVolatile); - io.map("isFinal", I.IsFinal); - io.map("isNodiscard", I.IsNodiscard); - io.map("isExplicitObjectMemberFunction", I.IsExplicitObjectMemberFunction); - if (I.Constexpr != ConstexprKind::None) - { - io.map("constexprKind", I.Constexpr); - } - if (I.StorageClass != StorageClassKind::None) - { - io.map("storageClass", I.StorageClass); - } - if (I.RefQualifier != ReferenceKind::None) - { - io.map("refQualifier", I.RefQualifier); - } - io.map("class", I.Class); - io.defer("params", [&]{ return dom::newArray(I.Params, *domCorpus_); }); - io.defer("return", [&]{ return domCreate(I.ReturnType, *domCorpus_); }); - io.defer("template", [&]{ return domCreate(I.Template, *domCorpus_); }); - io.map("overloadedOperator", I.OverloadedOperator); - io.defer("exceptionSpec", [&]{ return toString(I.Noexcept); }); - io.defer("explicitSpec", [&]{ return toString(I.Explicit); }); - if (!I.Requires.Written.empty()) - { - io.map("requires", I.Requires.Written); - } - } - if constexpr (T::isTypedef()) - { - io.defer("type", [&]{ return domCreate(I.Type, *domCorpus_); }); - io.defer("template", [&]{ return domCreate(I.Template, *domCorpus_); }); - io.map("isUsing", I.IsUsing); - } - if constexpr (T::isVariable()) - { - io.defer("type", [&]{ return domCreate(I.Type, *domCorpus_); }); - io.defer("template", [&]{ return domCreate(I.Template, *domCorpus_); }); - if (I.Constexpr != ConstexprKind::None) - { - io.map("constexprKind", I.Constexpr); - } - if (I.StorageClass != StorageClassKind::None) - { - io.map("storageClass", I.StorageClass); - } - io.map("isConstinit", I.IsConstinit); - io.map("isThreadLocal", I.IsThreadLocal); - if (!I.Initializer.Written.empty()) - { - io.map("initializer", I.Initializer.Written); - } - } - if constexpr (T::isField()) - { - io.defer("type", [&]{ return domCreate(I.Type, *domCorpus_); }); - if (!I.Default.Written.empty()) - { - io.map("default", I.Default.Written); - } - io.map("isMaybeUnused", I.IsMaybeUnused); - io.map("isDeprecated", I.IsDeprecated); - io.map("isVariant", I.IsVariant); - io.map("isMutable", I.IsMutable); - io.map("isBitfield", I.IsBitfield); - io.map("hasNoUniqueAddress", I.HasNoUniqueAddress); - if (I.IsBitfield) - { - io.map("bitfieldWidth", I.BitfieldWidth.Written); - } - } - if constexpr (T::isSpecialization()) - {} - if constexpr (T::isFriend()) - { - if (I.FriendSymbol) - { - io.defer("name", [&]{ return domCorpus_->get(I.FriendSymbol).get("name"); }); - io.defer("symbol", [&]{ return domCorpus_->get(I.FriendSymbol); }); - } - else if (I.FriendType) - { - io.defer("name", [&]{ return domCreate(I.FriendType, *domCorpus_).get("name"); }); - io.defer("type", [&]{ return domCreate(I.FriendType, *domCorpus_); }); - } - } - if constexpr (T::isAlias()) - { - MRDOCS_ASSERT(I.AliasedSymbol); - io.defer("aliasedSymbol", [&]{ return domCreate(I.AliasedSymbol, *domCorpus_); }); - } - if constexpr (T::isUsing()) - { - io.map("class", I.Class); - io.defer("shadows", [&]{ return dom::newArray(I.UsingSymbols, *domCorpus_); }); - io.defer("qualifier", [&]{ return domCreate(I.Qualifier, *domCorpus_); }); - } - if constexpr (T::isEnumerator()) - { - if (!I.Initializer.Written.empty()) - { - io.map("initializer", I.Initializer.Written); - } - } - if constexpr (T::isGuide()) - { - io.defer("params", [&]{ return dom::newArray(I.Params, *domCorpus_); }); - io.defer("deduced", [&]{ return domCreate(I.Deduced, *domCorpus_); }); - io.defer("template", [&]{ return domCreate(I.Template, *domCorpus_); }); - io.defer("explicitSpec", [&]{ return toString(I.Explicit); }); - } - if constexpr (T::isConcept()) - { - io.defer("template", [&]{ return domCreate(I.Template, *domCorpus_); }); - if (!I.Constraint.Written.empty()) - { - io.map("constraint", I.Constraint.Written); - } - } - } - }; -} dom::Object DomCorpus:: construct(Info const& I) const { - return visit(I, - [&](T const& I) - { - return dom::newObject>(I, dom::MappingTraits(*this)); - }); + return dom::ValueFrom(I, this).getObject(); } dom::Value @@ -1016,9 +125,9 @@ getJavadoc( dom::Object DomCorpus:: getOverloads( - OverloadSet const& os) const + OverloadSet const& overloads) const { - return domCreate(os, *this); + return dom::ValueFrom(overloads, this).getObject(); } } // mrdocs diff --git a/src/lib/Metadata/Function.cpp b/src/lib/Metadata/Function.cpp index 93272dbaa..e111930b6 100644 --- a/src/lib/Metadata/Function.cpp +++ b/src/lib/Metadata/Function.cpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace clang { @@ -148,6 +149,29 @@ toString( } } +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + Param const& p, + DomCorpus const*) +{ + io.map("name", dom::stringOrNull(p.Name)); + io.map("type", p.Type); + io.map("default", dom::stringOrNull(p.Default)); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Param const& p, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(p, domCorpus); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Info.cpp b/src/lib/Metadata/Info.cpp index 08d8cee29..f3cf33181 100644 --- a/src/lib/Metadata/Info.cpp +++ b/src/lib/Metadata/Info.cpp @@ -10,7 +10,11 @@ // #include "lib/Support/Radix.hpp" +#include "lib/Dom/LazyObject.hpp" +#include "lib/Dom/LazyArray.hpp" #include +#include +#include #include #include #include @@ -33,5 +37,243 @@ toString(InfoKind kind) noexcept } } +/* Customization to map Info types to DOM objects + + This function maps an Info type to a DOM object. + It includes all members of the derived type. + + The traits store a reference to the DomCorpus + so that it can resolve symbol IDs to the corresponding + Info objects. + + Whenever a member refers to symbol IDs, + the mapping trait will automatically resolve the + symbol ID to the corresponding Info object. + + This allows all references to be resolved to the + corresponding Info object lazily from the templates + that use the DOM. + */ +template +requires std::derived_from +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + InfoTy const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(domCorpus); + io.map("id", I.id); + if (!I.Name.empty()) + { + io.map("name", I.Name); + } + io.map("kind", I.Kind); + io.map("access", I.Access); + io.map("implicit", I.Implicit); + io.map("namespace", dom::LazyArray(I.Namespace, domCorpus)); + if (!I.Namespace.empty()) + { + io.map("parent", I.Namespace.front()); + } + if (I.javadoc) + { + io.map("doc", *I.javadoc); + } + using T = std::remove_cvref_t; + if constexpr(std::derived_from) + { + io.map("members", dom::LazyArray(I.Members, domCorpus)); + io.defer("overloads", [&]{ + // Eager array with overloadset or symbol + return generateScopeOverloadsArray(I, *domCorpus); + }); + } + if constexpr(std::derived_from) + { + io.map("loc", static_cast(I)); + } + if constexpr(T::isNamespace()) + { + io.defer("interface", [&I, domCorpus]{ + // Eager object with each Info type + auto t = std::make_shared(makeTranche(I, **domCorpus)); + return dom::ValueFrom(t, domCorpus); + }); + io.map("usingDirectives", dom::LazyArray(I.UsingDirectives, domCorpus)); + } + if constexpr (T::isRecord()) + { + io.map("tag", I.KeyKind); + io.map("defaultAccess", getDefaultAccessString(I.KeyKind)); + io.map("isTypedef", I.IsTypeDef); + io.map("bases", dom::LazyArray(I.Bases, domCorpus)); + io.defer("interface", [domCorpus, &I] { + // Eager object with each Info type for each access specifier + auto sp = std::make_shared(makeInterface(I, domCorpus->getCorpus())); + return dom::ValueFrom(sp, domCorpus); + }); + io.map("template", I.Template); + } + if constexpr (T::isEnum()) + { + io.map("type", I.UnderlyingType); + io.map("isScoped", I.Scoped); + } + if constexpr (T::isFunction()) + { + io.map("isVariadic", I.IsVariadic); + io.map("isVirtual", I.IsVirtual); + io.map("isVirtualAsWritten", I.IsVirtualAsWritten); + io.map("isPure", I.IsPure); + io.map("isDefaulted", I.IsDefaulted); + io.map("isExplicitlyDefaulted", I.IsExplicitlyDefaulted); + io.map("isDeleted", I.IsDeleted); + io.map("isDeletedAsWritten", I.IsDeletedAsWritten); + io.map("isNoReturn", I.IsNoReturn); + io.map("hasOverrideAttr", I.HasOverrideAttr); + io.map("hasTrailingReturn", I.HasTrailingReturn); + io.map("isConst", I.IsConst); + io.map("isVolatile", I.IsVolatile); + io.map("isFinal", I.IsFinal); + io.map("isNodiscard", I.IsNodiscard); + io.map("isExplicitObjectMemberFunction", I.IsExplicitObjectMemberFunction); + if (I.Constexpr != ConstexprKind::None) + { + io.map("constexprKind", I.Constexpr); + } + if (I.StorageClass != StorageClassKind::None) + { + io.map("storageClass", I.StorageClass); + } + if (I.RefQualifier != ReferenceKind::None) + { + io.map("refQualifier", I.RefQualifier); + } + io.map("class", I.Class); + io.map("params", dom::LazyArray(I.Params, domCorpus)); + io.map("return", I.ReturnType); + io.map("template", I.Template); + io.map("overloadedOperator", I.OverloadedOperator); + io.map("exceptionSpec", I.Noexcept); + io.map("explicitSpec", I.Explicit); + if (!I.Requires.Written.empty()) + { + io.map("requires", I.Requires.Written); + } + } + if constexpr (T::isTypedef()) + { + io.map("type", I.Type); + io.map("template", I.Template); + io.map("isUsing", I.IsUsing); + } + if constexpr (T::isVariable()) + { + io.map("type", I.Type); + io.map("template", I.Template); + if (I.Constexpr != ConstexprKind::None) + { + io.map("constexprKind", I.Constexpr); + } + if (I.StorageClass != StorageClassKind::None) + { + io.map("storageClass", I.StorageClass); + } + io.map("isConstinit", I.IsConstinit); + io.map("isThreadLocal", I.IsThreadLocal); + if (!I.Initializer.Written.empty()) + { + io.map("initializer", I.Initializer.Written); + } + } + if constexpr (T::isField()) + { + io.map("type", I.Type); + if (!I.Default.Written.empty()) + { + io.map("default", I.Default.Written); + } + io.map("isMaybeUnused", I.IsMaybeUnused); + io.map("isDeprecated", I.IsDeprecated); + io.map("isVariant", I.IsVariant); + io.map("isMutable", I.IsMutable); + io.map("isBitfield", I.IsBitfield); + io.map("hasNoUniqueAddress", I.HasNoUniqueAddress); + if (I.IsBitfield) + { + io.map("bitfieldWidth", I.BitfieldWidth.Written); + } + } + if constexpr (T::isSpecialization()) + {} + if constexpr (T::isFriend()) + { + if (I.FriendSymbol) + { + io.defer("name", [&I, domCorpus]{ + return dom::ValueFrom(I.FriendSymbol, domCorpus).get("name"); + }); + io.map("symbol", I.FriendSymbol); + } + else if (I.FriendType) + { + io.defer("name", [&]{ + return dom::ValueFrom(I.FriendType, domCorpus).get("name"); + }); + io.map("type", I.FriendType); + } + } + if constexpr (T::isAlias()) + { + MRDOCS_ASSERT(I.AliasedSymbol); + io.map("aliasedSymbol", I.AliasedSymbol); + } + if constexpr (T::isUsing()) + { + io.map("class", I.Class); + io.map("shadows", dom::LazyArray(I.UsingSymbols, domCorpus)); + io.map("qualifier", I.Qualifier); + } + if constexpr (T::isEnumerator()) + { + if (!I.Initializer.Written.empty()) + { + io.map("initializer", I.Initializer.Written); + } + } + if constexpr (T::isGuide()) + { + io.map("params", dom::LazyArray(I.Params, domCorpus)); + io.map("deduced", I.Deduced); + io.map("template", I.Template); + io.map("explicitSpec", I.Explicit); + } + if constexpr (T::isConcept()) + { + io.map("template", I.Template); + if (!I.Constraint.Written.empty()) + { + io.map("constraint", I.Constraint.Written); + } + } +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Info const& I, + DomCorpus const* domCorpus) +{ + return visit(I, + [&](T const& I) + { + v = dom::LazyObject(I, domCorpus); + }); +} + + } // mrdocs } // clang diff --git a/src/lib/Metadata/Interface.cpp b/src/lib/Metadata/Interface.cpp index 0cb3abbf4..5204b752a 100644 --- a/src/lib/Metadata/Interface.cpp +++ b/src/lib/Metadata/Interface.cpp @@ -11,7 +11,9 @@ #include "lib/Lib/ConfigImpl.hpp" #include "lib/Support/Debug.hpp" +#include "lib/Dom/LazyArray.hpp" #include +#include #include #include #include @@ -363,6 +365,118 @@ makeTranche( return T; } +/* A DOM object that represents a tranche + + This function creates an Interface object for a given + record. The Interface object is used to generate the + "interface" value of the DOM for symbols that represent + records or namespaces. + + The interface is not part of the Corpus. It is a temporary + structure generated to aggregate the symbols of a record. + This structure is provided to the user via the DOM. + */ +class DomTranche : public dom::DefaultObjectImpl +{ + std::shared_ptr tranche_; + + static + dom::Value + init( + std::span list, + DomCorpus const& domCorpus) + { + return dom::LazyArray(list, [&](SymbolID const& id) + { + return domCorpus.get(id); + }); + } + + static + dom::Value + init( + const ScopeInfo& scope, + DomCorpus const& domCorpus) + { + return generateScopeOverloadsArray(scope, domCorpus); + } + + +public: + DomTranche( + std::shared_ptr const& tranche, + DomCorpus const& domCorpus) noexcept + : dom::DefaultObjectImpl({ + #define INFO(Plural, LC_Plural) \ + { #LC_Plural, init(tranche->Plural, domCorpus) }, + #include + { "types", init(tranche->Types, domCorpus) }, + { "staticfuncs", init(tranche->StaticFunctions, domCorpus) }, + { "overloads", init(tranche->Overloads, domCorpus) }, + { "staticoverloads", init(tranche->StaticOverloads, domCorpus) }, + }) + , tranche_(tranche) + { + } +}; + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::shared_ptr const& sp, + DomCorpus const* domCorpus) +{ + /* Unfortunately, we cannot use LazyObject like we do + in DomCorpus because the Tranche object is not + part of the Corpus. + + We must create a new object and eagerly populate it + with the values from the Tranche object. + + Unfortunately, we cannot use the default + dom::Object type either. + + We also need a custom object impl type because we + need to store a shared_ptr to the Tranche object + to keep it alive. + */ + if (!sp) + { + v = nullptr; + return; + } + v = dom::newObject(sp, *domCorpus); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::shared_ptr const& sp, + DomCorpus const* domCorpus) +{ + /* Unfortunately, we cannot use LazyObject like we do + in DomCorpus because the Interface object is not + part of the Corpus. + + We must create a new object and eagerly populate it + with the values from the individual Tranche objects. + */ + if (!sp) + { + v = nullptr; + return; + } + // The dom::Value for each tranche will keep the + // respective shared_ptr alive. + v = dom::Object({ + { "public", dom::ValueFrom(sp->Public, domCorpus) }, + { "protected", dom::ValueFrom(sp->Protected, domCorpus) }, + { "private", dom::ValueFrom(sp->Private, domCorpus) } + }); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Javadoc.cpp b/src/lib/Metadata/Javadoc.cpp index 753e39f92..9e0e11638 100644 --- a/src/lib/Metadata/Javadoc.cpp +++ b/src/lib/Metadata/Javadoc.cpp @@ -13,6 +13,7 @@ #include "lib/Support/Debug.hpp" #include #include +#include #include #include #include @@ -380,5 +381,17 @@ append(doc::List&& blocks) #endif } +/** Return the Javadoc as a @ref dom::Value. + */ +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Javadoc const& doc, + DomCorpus const* domCorpus) +{ + v = domCorpus->getJavadoc(doc); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Name.cpp b/src/lib/Metadata/Name.cpp index bc85a73b0..9e2677a05 100644 --- a/src/lib/Metadata/Name.cpp +++ b/src/lib/Metadata/Name.cpp @@ -9,6 +9,9 @@ // #include +#include +#include +#include #include namespace clang { @@ -94,5 +97,36 @@ toString(const NameInfo& N) return result; } +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + NameInfo const& I, + DomCorpus const* domCorpus) +{ + io.map("kind", I.Kind); + visit(I, [domCorpus, &io](const T& t) + { + io.map("name", t.Name); + io.map("symbol", t.id); + if constexpr(requires { t.TemplateArgs; }) + { + io.map("args", dom::LazyArray(t.TemplateArgs, domCorpus)); + } + io.map("prefix", t.Prefix); + }); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NameInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Overloads.cpp b/src/lib/Metadata/Overloads.cpp index a16857f66..13731fa21 100644 --- a/src/lib/Metadata/Overloads.cpp +++ b/src/lib/Metadata/Overloads.cpp @@ -12,11 +12,35 @@ #include #include #include +#include +#include #include #include namespace clang { namespace mrdocs { +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + OverloadSet const& overloads, + DomCorpus const* domCorpus) +{ + /* Unfortunately, this can't use LazyObject like all other + * Corpus types because the overload sets are not directly + * available from the corpus. + * The `overloads` value is a temporary reference created + * by the `Info` tag_invoke. + */ + v = dom::Object({ + { "kind", "overload"}, + { "name", overloads.Name }, + { "members", dom::LazyArray(overloads.Members, domCorpus) }, + { "namespace", dom::LazyArray(overloads.Namespace, domCorpus) }, + { "parent", domCorpus->get(overloads.Parent) } + }); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Record.cpp b/src/lib/Metadata/Record.cpp index f8bcd6070..1dd491ad9 100644 --- a/src/lib/Metadata/Record.cpp +++ b/src/lib/Metadata/Record.cpp @@ -9,6 +9,7 @@ // #include +#include namespace clang { namespace mrdocs { @@ -30,5 +31,28 @@ toString( } } +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + BaseInfo const& I, + DomCorpus const* domCorpus) +{ + io.map("access", I.Access); + io.map("isVirtual", I.IsVirtual); + io.map("type", dom::ValueFrom(I.Type, domCorpus)); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + BaseInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Scope.cpp b/src/lib/Metadata/Scope.cpp new file mode 100644 index 000000000..f53b0dc11 --- /dev/null +++ b/src/lib/Metadata/Scope.cpp @@ -0,0 +1,48 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +dom::Array +generateScopeOverloadsArray( + ScopeInfo const& I, + DomCorpus const& domCorpus) +{ + /* Unfortunately, this information is not readily + available in the Corpus, so we can't have lazy + references to these members like we do for + other Info types. + */ + dom::Array res; + domCorpus.getCorpus().traverseOverloads(I, [&](const auto& C) + { + using BareT = std::remove_cvref_t; + static_assert(std::is_base_of_v || std::is_same_v); + if constexpr(std::is_base_of_v) + { + res.push_back(domCorpus.get(C.id)); + } + else /* if constexpr(std::is_same_v) */ + { + res.push_back(domCorpus.getOverloads(C)); + } + }); + return res; +} + + +} // mrdocs +} // clang diff --git a/src/lib/Metadata/Source.cpp b/src/lib/Metadata/Source.cpp index 82691fff8..1b77dccfa 100644 --- a/src/lib/Metadata/Source.cpp +++ b/src/lib/Metadata/Source.cpp @@ -10,6 +10,8 @@ // #include +#include "lib/Dom/LazyObject.hpp" +#include "lib/Dom/LazyArray.hpp" namespace clang { namespace mrdocs { @@ -30,5 +32,54 @@ toString(FileKind kind) }; } +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + Location const& loc) +{ + io.map("path", loc.Path); + io.map("file", loc.Filename); + io.map("line", loc.LineNumber); + io.map("kind", loc.Kind); + io.map("documented", loc.Documented); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + Location const& loc) +{ + v = dom::LazyObject(loc); +} + +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + SourceInfo const& I) +{ + if (I.DefLoc) + { + io.map("def", *I.DefLoc); + } + if (!I.Loc.empty()) + { + io.map("decl", dom::LazyArray(I.Loc)); + } +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SourceInfo const& I) +{ + v = dom::LazyObject(I); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Symbols.cpp b/src/lib/Metadata/Symbols.cpp index 2163ab75b..5e157b970 100644 --- a/src/lib/Metadata/Symbols.cpp +++ b/src/lib/Metadata/Symbols.cpp @@ -10,7 +10,11 @@ // #include +#include +#include +#include #include +#include namespace clang { namespace mrdocs { @@ -86,5 +90,41 @@ compareSymbolNames( return std::strong_ordering::equivalent; } +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SymbolID const& id) +{ + v = toBase16(id); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SymbolID const& id, + DomCorpus const* domCorpus) +{ + v = domCorpus->get(id); +} + +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + std::unique_ptr const& t, + DomCorpus const* domCorpus) +{ + if (!t) + { + v = nullptr; + return; + } + v = dom::ValueFrom(*t, domCorpus); +} + + } // mrdocs } // clang diff --git a/src/lib/Metadata/Template.cpp b/src/lib/Metadata/Template.cpp index 2773b1c84..0aa7f36a3 100644 --- a/src/lib/Metadata/Template.cpp +++ b/src/lib/Metadata/Template.cpp @@ -11,6 +11,10 @@ // #include +#include +#include +#include +#include namespace clang { namespace mrdocs { @@ -109,5 +113,115 @@ toString( }); } +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + TArg const& I, + DomCorpus const* domCorpus) +{ + io.map("kind", toString(I.Kind)); + io.map("is-pack", I.IsPackExpansion); + visit(I, [domCorpus, &io](const T& t) { + if constexpr(T::isType()) + { + io.map("type", t.Type); + } + if constexpr(T::isNonType()) + { + io.map("value", t.Value.Written); + } + if constexpr(T::isTemplate()) + { + io.map("name", t.Name); + io.map("template", t.Template); + } + }); +} + + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TArg const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + TParam const& I, + DomCorpus const* domCorpus) +{ + io.map("kind", toString(I.Kind)); + io.map("name", dom::stringOrNull(I.Name)); + io.map("is-pack", I.IsParameterPack); + visit(I, [domCorpus, &io](const T& t) { + if(t.Default) + { + io.map("default", t.Default); + } + if constexpr(T::isType()) + { + io.map("key", t.KeyKind); + if (t.Constraint) + { + io.map("constraint", t.Constraint); + } + } + if constexpr(T::isNonType()) + { + io.map("type", t.Type); + } + if constexpr(T::isTemplate()) + { + io.map("params", dom::LazyArray(t.Params, domCorpus)); + } + }); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TParam const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + TemplateInfo const& I, + DomCorpus const* domCorpus) +{ + io.defer("kind", [&] { + return toString(I.specializationKind()); + }); + io.map("primary", I.Primary); + io.map("params", dom::LazyArray(I.Params, domCorpus)); + io.map("args", dom::LazyArray(I.Args, domCorpus)); + io.map("requires", dom::stringOrNull(I.Requires.Written)); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TemplateInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // mrdocs } // clang diff --git a/src/lib/Metadata/Type.cpp b/src/lib/Metadata/Type.cpp index 48b4c97f5..166cccf39 100644 --- a/src/lib/Metadata/Type.cpp +++ b/src/lib/Metadata/Type.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include namespace clang { namespace mrdocs { @@ -306,5 +308,75 @@ toString( return write(); } +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + TypeInfo const& I, + DomCorpus const* domCorpus) +{ + io.map("kind", I.Kind); + io.map("is-pack", I.IsPackExpansion); + visit(I, [&io, domCorpus](const T& t) + { + if constexpr(T::isNamed()) + { + io.map("name", t.Name); + } + if constexpr(T::isDecltype()) + { + io.map("operand", t.Operand.Written); + } + if constexpr(T::isAuto()) + { + io.map("keyword", t.Keyword); + if (t.Constraint) + { + io.map("constraint", t.Constraint); + } + } + if constexpr(requires { t.CVQualifiers; }) + { + io.map("cv-qualifiers", t.CVQualifiers); + } + if constexpr(requires { t.ParentType; }) + { + io.map("parent-type", t.ParentType); + } + if constexpr(requires { t.PointeeType; }) + { + io.map("pointee-type", t.PointeeType); + } + if constexpr(T::isArray()) + { + io.map("element-type", t.ElementType); + if(t.Bounds.Value) + { + io.map("bounds-value", *t.Bounds.Value); + } + io.map("bounds-expr", t.Bounds.Written); + } + if constexpr(T::isFunction()) + { + io.map("return-type", t.ReturnType); + io.map("param-types", dom::LazyArray(t.ParamTypes, domCorpus)); + io.map("exception-spec", t.ExceptionSpec); + io.map("ref-qualifier", t.RefQualifier); + io.map("is-variadic", t.IsVariadic); + } + }); +} + +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TypeInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // mrdocs } // clang diff --git a/src/test/lib/Dom/Dom.cpp b/src/test/lib/Dom/Dom.cpp index a8fcd5c1c..59dd90aeb 100644 --- a/src/test/lib/Dom/Dom.cpp +++ b/src/test/lib/Dom/Dom.cpp @@ -1109,14 +1109,6 @@ struct Dom_test } } - // Value(Enum v) - { - enum class E { A, B, C }; - Value v(E::A); - BOOST_TEST(v.isInteger()); - BOOST_TEST(v == 0); - } - // Value(char const(&sz)[N]) { Value v("hello"); diff --git a/src/test/lib/Dom/LazyObject.cpp b/src/test/lib/Dom/LazyObject.cpp index 72b7e28f7..bbc4960c0 100644 --- a/src/test/lib/Dom/LazyObject.cpp +++ b/src/test/lib/Dom/LazyObject.cpp @@ -20,14 +20,14 @@ struct Y { std::string b = "world"; }; -template <> -struct ToValue { - Value - operator()(Y const& y) const - { - return y.a + " " + y.b; - } -}; +void +tag_invoke( + ValueFromTag, + Value& v, + Y const& y) +{ + v = y.a + " " + y.b; +} struct X { int i = 123; @@ -35,18 +35,18 @@ struct X { Y y; }; -template<> -struct MappingTraits +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + X const& x) { - template - void map(IO &io, X const& x) const - { - io.map("i", x.i); - io.map("s", x.s); - io.defer("si", [&x]{ return x.s + std::to_string(x.i); }); - io.map("y", x.y); - } -}; + io.map("i", x.i); + io.map("s", x.s); + io.defer("si", [&x]{ return x.s + std::to_string(x.i); }); + io.map("y", x.y); +} struct LazyObject_test { @@ -194,8 +194,7 @@ struct LazyObject_test obj.set("x", 789); std::size_t count = 0; bool found = false; - bool match = true; - obj.visit([&count, &match, &found](String key, Value value) { + obj.visit([&count, &found](String key, Value value) { if (key == "x" && value == 789) found = true; ++count; diff --git a/util/generate-config-info.py b/util/generate-config-info.py index 7e729e68d..808e06dbc 100644 --- a/util/generate-config-info.py +++ b/util/generate-config-info.py @@ -302,7 +302,7 @@ def generate_public_settings_hpp(config): contents += ',\n' contents += f' {cpp_type}' contents += f'> {to_camel_case("default")}Value = std::monostate();\n' - contents += f' std::string {to_camel_case("relativeto")};\n' + contents += f' std::string {to_camel_case("relativeto")} = {{}};\n' contents += ' };\n\n' contents += ' /** Normalize the configuration values with a visitor\n'