Skip to content

Commit d8c7267

Browse files
committed
refactor(metadata): integrate Boost.Describe to remove manual mapping boilerplate
This introduces Boost.Describe and Boost.Mp11 and applies them across metadata types (symbols, enums, inline elements) to replace hand-written io.map() calls with reflection-based code. This improves maintainability without altering the public API or user-facing features.
1 parent c3dbded commit d8c7267

38 files changed

+648
-243
lines changed

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,36 @@ jobs:
432432
run: |
433433
rm -r ../third-party/llvm-project
434434
435+
- name: Install Boost.Describe
436+
uses: alandefreitas/cpp-actions/[email protected]
437+
with:
438+
source-dir: ../build/third-party/source/boost_describe
439+
git-repository: https://github.com/boostorg/describe
440+
git-tag: boost-1.89.0
441+
build-dir: ${sourceDir}/build
442+
cc: ${{ steps.setup-cpp.outputs.cc }}
443+
cxx: ${{ steps.setup-cpp.outputs.cxx }}
444+
build-type: Release
445+
shared: false
446+
install: false
447+
run-tests: false
448+
trace-commands: true
449+
450+
- name: Install Boost.Mp11
451+
uses: alandefreitas/cpp-actions/[email protected]
452+
with:
453+
source-dir: ..build/third-party/source/boost_mp11
454+
git-repository: https://github.com/boostorg/mp11
455+
git-tag: boost-1.89.0
456+
build-dir: ${sourceDir}/build
457+
cc: ${{ steps.setup-cpp.outputs.cc }}
458+
cxx: ${{ steps.setup-cpp.outputs.cxx }}
459+
build-type: Release
460+
shared: false
461+
install: false
462+
run-tests: false
463+
trace-commands: true
464+
435465
- name: Install Duktape
436466
uses: alandefreitas/cpp-actions/[email protected]
437467
with:

CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,20 @@ llvm_map_components_to_libnames(llvm_libs all)
277277
string(REGEX REPLACE " /W[0-4]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
278278
string(REGEX REPLACE " /W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
279279

280+
# Boost.Describe + Boost.Mp11 (header-only, fetched via bootstrap.py recipes)
281+
set(BOOST_DESCRIBE_ROOT "${CMAKE_SOURCE_DIR}/build/third-party/source/boost_describe")
282+
set(BOOST_MP11_ROOT "${CMAKE_SOURCE_DIR}/build/third-party/source/boost_mp11")
283+
284+
add_library(boost_mp11 INTERFACE)
285+
target_include_directories(boost_mp11 INTERFACE "${BOOST_MP11_ROOT}/include")
286+
287+
add_library(boost_describe INTERFACE)
288+
target_include_directories(boost_describe INTERFACE "${BOOST_DESCRIBE_ROOT}/include")
289+
target_link_libraries(boost_describe INTERFACE boost_mp11)
290+
set_target_properties(boost_describe boost_mp11 PROPERTIES
291+
EXPORT_NAME ""
292+
)
293+
280294
# Duktape
281295
find_package(duktape CONFIG)
282296
if (NOT DUKTAPE_FOUND)
@@ -322,6 +336,8 @@ target_include_directories(mrdocs-core
322336
PUBLIC
323337
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>"
324338
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include/>"
339+
"$<BUILD_INTERFACE:${BOOST_DESCRIBE_ROOT}/include>"
340+
"$<BUILD_INTERFACE:${BOOST_MP11_ROOT}/include>"
325341
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
326342
PRIVATE
327343
"${PROJECT_SOURCE_DIR}/src"

include/mrdocs/Dom/LazyArray.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
55
//
66
// Copyright (c) 2024 Alan de Freitas ([email protected])
7+
// Copyright (c) 2025 Gennaro Prota ([email protected])
78
//
89
// Official repository: https://github.com/cppalliance/mrdocs
910
//
@@ -211,6 +212,26 @@ TransformArray(T const& arr, F const& f)
211212
}
212213

213214
} // dom
215+
216+
class DomCorpus;
217+
218+
/** Map a vector of strings to a @ref dom::Value object.
219+
220+
@param v The output parameter to receive the dom::Value.
221+
@param elems The vector of T's to convert.
222+
@param domCorpus The DomCorpus used to resolve references.
223+
*/
224+
template <typename T>
225+
void
226+
tag_invoke(
227+
dom::ValueFromTag,
228+
dom::Value& v,
229+
std::vector<T> const& elems,
230+
DomCorpus const* domCorpus)
231+
{
232+
v = dom::LazyArray(elems, domCorpus);
233+
}
234+
214235
} // mrdocs
215236

216237

include/mrdocs/Metadata/DocComment/Inline/ImageInline.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
// Copyright (c) 2025 Alan de Freitas ([email protected])
8+
// Copyright (c) 2025 Gennaro Prota ([email protected])
89
//
910
// Official repository: https://github.com/cppalliance/mrdocs
1011
//
1112

1213
#ifndef MRDOCS_API_METADATA_DOCCOMMENT_INLINE_IMAGEINLINE_HPP
1314
#define MRDOCS_API_METADATA_DOCCOMMENT_INLINE_IMAGEINLINE_HPP
1415

16+
#include <boost/describe/class.hpp>
1517
#include <mrdocs/Platform.hpp>
1618
#include <mrdocs/ADT/Polymorphic.hpp>
1719
#include <mrdocs/Metadata/DocComment/Inline/TextInline.hpp>
20+
#include <mrdocs/Metadata/Symbol/SymbolDescribeMapper.hpp>
1821
#include <string>
1922

2023
namespace mrdocs::doc {
@@ -52,6 +55,12 @@ struct ImageInline final
5255
bool operator==(ImageInline const&) const noexcept = default;
5356
};
5457

58+
BOOST_DESCRIBE_STRUCT(
59+
ImageInline,
60+
(InlineCommonBase<InlineKind::Image>, InlineContainer),
61+
(src, alt)
62+
)
63+
5564
/** Map the @ref ImageInline to a @ref dom::Object.
5665
5766
@param t The tag.
@@ -69,8 +78,7 @@ tag_invoke(
6978
{
7079
tag_invoke(t, io, dynamic_cast<Inline const&>(I), domCorpus);
7180
tag_invoke(t, io, dynamic_cast<InlineContainer const&>(I), domCorpus);
72-
io.map("src", I.src);
73-
io.map("alt", I.alt);
81+
mapWithDescribe(io, I, domCorpus);
7482
}
7583

7684
/** Return the @ref ImageInline as a @ref dom::Value object.

include/mrdocs/Metadata/Expression.hpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
55
//
66
// Copyright (c) 2023 Krystian Stasiowski ([email protected])
7+
// Copyright (c) 2025 Gennaro Prota ([email protected])
78
//
89
// Official repository: https://github.com/cppalliance/mrdocs
910
//
@@ -13,12 +14,17 @@
1314

1415
#include <mrdocs/Platform.hpp>
1516
#include <mrdocs/ADT/Optional.hpp>
17+
#include <mrdocs/Dom/Array.hpp>
18+
#include <mrdocs/Dom/LazyObject.hpp>
19+
#include <mrdocs/Dom/Value.hpp>
1620
#include <concepts>
1721
#include <optional>
1822
#include <string>
1923

2024
namespace mrdocs {
2125

26+
class DomCorpus;
27+
2228
/** Represents an expression
2329
*/
2430
struct ExprInfo
@@ -90,6 +96,39 @@ static void merge(
9096
}
9197
}
9298

99+
/** Map an ExprInfo to a @ref dom::Value object.
100+
101+
@param v The output parameter to receive the dom::Value.
102+
@param expr The expression info to convert.
103+
*/
104+
inline
105+
void
106+
tag_invoke(
107+
dom::ValueFromTag,
108+
dom::Value& v,
109+
ExprInfo const& expr,
110+
DomCorpus const*)
111+
{
112+
v = expr.Written;
113+
}
114+
115+
/** Map an ExprInfo to a @ref dom::Value object.
116+
117+
@param v The output parameter to receive the dom::Value.
118+
@param expr The expression info to convert.
119+
*/
120+
inline
121+
void
122+
tag_invoke(
123+
dom::LazyObjectMapTag,
124+
dom::Value& v,
125+
ExprInfo const& expr,
126+
DomCorpus const*
127+
)
128+
{
129+
v = expr.Written;
130+
}
131+
93132
} // mrdocs
94133

95134
#endif

include/mrdocs/Metadata/Symbol/Concept.hpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@
44
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
55
//
66
// Copyright (c) 2024 Krystian Stasiowski ([email protected])
7+
// Copyright (c) 2025 Gennaro Prota ([email protected])
78
//
89
// Official repository: https://github.com/cppalliance/mrdocs
910
//
1011

1112
#ifndef MRDOCS_API_METADATA_SYMBOL_CONCEPT_HPP
1213
#define MRDOCS_API_METADATA_SYMBOL_CONCEPT_HPP
1314

15+
#include <boost/describe/class.hpp>
1416
#include <mrdocs/Platform.hpp>
1517
#include <mrdocs/ADT/Optional.hpp>
1618
#include <mrdocs/ADT/Polymorphic.hpp>
1719
#include <mrdocs/Metadata/Expression.hpp>
1820
#include <mrdocs/Metadata/Symbol.hpp>
1921
#include <mrdocs/Metadata/Symbol/Source.hpp>
22+
#include <mrdocs/Metadata/Symbol/SymbolDescribeMapper.hpp>
2023
#include <mrdocs/Metadata/Template.hpp>
2124

2225
namespace mrdocs {
@@ -49,6 +52,12 @@ struct ConceptSymbol final
4952
operator<=>(ConceptSymbol const& other) const;
5053
};
5154

55+
BOOST_DESCRIBE_STRUCT(
56+
ConceptSymbol,
57+
(SymbolCommonBase<SymbolKind::Concept>),
58+
(Template, Constraint)
59+
)
60+
5261
/** Merge another ConceptSymbol into this one.
5362
*/
5463
MRDOCS_DECL
@@ -71,11 +80,8 @@ tag_invoke(
7180
DomCorpus const* domCorpus)
7281
{
7382
tag_invoke(t, io, I.asInfo(), domCorpus);
74-
io.map("template", I.Template);
75-
if (!I.Constraint.Written.empty())
76-
{
77-
io.map("constraint", I.Constraint.Written);
78-
}
83+
mapWithDescribe(io, I, domCorpus);
84+
7985
}
8086

8187
/** Map the ConceptSymbol to a @ref dom::Value object.

include/mrdocs/Metadata/Symbol/Enum.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
// Copyright (c) 2023 Vinnie Falco ([email protected])
8+
// Copyright (c) 2025 Gennaro Prota ([email protected])
89
//
910
// Official repository: https://github.com/cppalliance/mrdocs
1011
//
1112

1213
#ifndef MRDOCS_API_METADATA_SYMBOL_ENUM_HPP
1314
#define MRDOCS_API_METADATA_SYMBOL_ENUM_HPP
1415

16+
#include <boost/describe/class.hpp>
1517
#include <mrdocs/Platform.hpp>
1618
#include <mrdocs/ADT/Polymorphic.hpp>
1719
#include <mrdocs/Dom/LazyArray.hpp>
1820
#include <mrdocs/Metadata/Symbol.hpp>
1921
#include <mrdocs/Metadata/Symbol/Source.hpp>
22+
#include <mrdocs/Metadata/Symbol/SymbolDescribeMapper.hpp>
2023
#include <mrdocs/Metadata/Type.hpp>
2124

2225
namespace mrdocs {
@@ -65,6 +68,12 @@ struct EnumSymbol final
6568
}
6669
};
6770

71+
BOOST_DESCRIBE_STRUCT(
72+
EnumSymbol,
73+
(SymbolCommonBase<SymbolKind::Enum>),
74+
(Scoped, UnderlyingType, Constants)
75+
)
76+
6877
/** Return the list of enum constants for this symbol.
6978
*/
7079
inline

include/mrdocs/Metadata/Symbol/EnumConstant.hpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
55
//
66
// Copyright (c) 2023 Krystian Stasiowski ([email protected])
7+
// Copyright (c) 2025 Gennaro Prota ([email protected])
78
//
89
// Official repository: https://github.com/cppalliance/mrdocs
910
//
1011

1112
#ifndef MRDOCS_API_METADATA_SYMBOL_ENUMCONSTANT_HPP
1213
#define MRDOCS_API_METADATA_SYMBOL_ENUMCONSTANT_HPP
1314

15+
#include <boost/describe/class.hpp>
1416
#include <mrdocs/Metadata/Expression.hpp>
1517
#include <mrdocs/Metadata/Symbol.hpp>
18+
#include <mrdocs/Metadata/Symbol/SymbolDescribeMapper.hpp>
1619
#include <mrdocs/Metadata/Symbol/Source.hpp>
1720

1821
namespace mrdocs {
@@ -36,6 +39,12 @@ struct EnumConstantSymbol final
3639
}
3740
};
3841

42+
BOOST_DESCRIBE_STRUCT(
43+
EnumConstantSymbol,
44+
(SymbolCommonBase<SymbolKind::EnumConstant>),
45+
(Initializer)
46+
)
47+
3948
/** Merge another EnumConstantSymbol into this one.
4049
@param I Destination symbol to update.
4150
@param Other Source symbol providing data.
@@ -60,10 +69,7 @@ tag_invoke(
6069
DomCorpus const* domCorpus)
6170
{
6271
tag_invoke(t, io, I.asInfo(), domCorpus);
63-
if (!I.Initializer.Written.empty())
64-
{
65-
io.map("initializer", I.Initializer.Written);
66-
}
72+
mapWithDescribe(io, I, domCorpus);
6773
}
6874

6975
/** Map the EnumConstantSymbol to a @ref dom::Value object.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// Copyright (c) 2025 Gennaro Prota ([email protected])
7+
//
8+
// Official repository: https://github.com/cppalliance/mrdocs
9+
//
10+
11+
#ifndef MRDOCS_METADATA_SYMBOL_ENUMTOSTRING_HPP
12+
#define MRDOCS_METADATA_SYMBOL_ENUMTOSTRING_HPP
13+
14+
#include <boost/describe/enumerators.hpp>
15+
#include <boost/mp11.hpp>
16+
#include <mrdocs/Support/Assert.hpp>
17+
#include <mrdocs/Support/String.hpp>
18+
#include <string>
19+
#include <type_traits>
20+
21+
namespace mrdocs {
22+
23+
/** Convert a Boost.Describe'd enumerator to string form.
24+
*/
25+
template <typename Enum>
26+
requires std::is_enum_v<Enum>
27+
std::string
28+
toString(Enum e) noexcept
29+
{
30+
std::string result;
31+
boost::mp11::mp_for_each<
32+
boost::describe::describe_enumerators<Enum>>(
33+
[&](auto const& D)
34+
{
35+
using Descriptor = std::decay_t<decltype(D)>;
36+
if (Descriptor::value == e)
37+
{
38+
result = toKebabCase(Descriptor::name);
39+
}
40+
});
41+
42+
if (!result.empty())
43+
{
44+
return result;
45+
}
46+
47+
MRDOCS_UNREACHABLE();
48+
}
49+
50+
}
51+
52+
#endif

0 commit comments

Comments
 (0)