welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
doc.hpp
Go to the documentation of this file.
1#pragma once
2#include <array>
3#include <concepts>
4#include <meta>
5#include <span>
6#include <string>
7#include <string_view>
8#include <vector>
9
10#include <welder/concepts.hpp> // the doc_style concept (over function_doc, below)
11
24
25namespace welder::inline v0 {
26
45consteval std::string cleandoc(std::string_view text) {
46 std::vector<std::string> lines{};
47 std::string cur{};
48 for (char c : text) {
49 if (c == '\n') {
50 lines.push_back(cur);
51 cur.clear();
52 } else
53 cur.push_back(c);
54 }
55 lines.push_back(cur);
56
57 auto leading = [](const std::string& s) {
58 std::string::size_type n{0};
59 while (n < s.size() && (s[n] == ' ' || s[n] == '\t'))
60 ++n;
61 return n;
62 };
63 auto blank = [](const std::string& s) {
64 for (char c : s)
65 if (c != ' ' && c != '\t' && c != '\r')
66 return false;
67 return true;
68 };
69
70 // Common leading whitespace of the lines *after* the first (blank lines don't
71 // constrain it), which is what gets removed from those lines.
72 bool have_margin{false};
73 std::string::size_type margin{0};
74 for (std::string::size_type i{1}; i < lines.size(); ++i)
75 if (!blank(lines[i])) {
76 const auto ind{leading(lines[i])};
77 if (!have_margin || ind < margin) {
78 margin = ind;
79 have_margin = true;
80 }
81 }
82
83 if (!lines.empty())
84 lines[0].erase(0, leading(lines[0]));
85 if (have_margin)
86 for (std::string::size_type i{1}; i < lines.size(); ++i)
87 lines[i].erase(0, margin); // over-erasing a short blank line is fine
88
89 while (!lines.empty() && blank(lines.back()))
90 lines.pop_back();
91 while (!lines.empty() && blank(lines.front()))
92 lines.erase(lines.begin());
93
94 std::string out{};
95 for (std::string::size_type i{0}; i < lines.size(); ++i) {
96 if (i)
97 out.push_back('\n');
98 out += lines[i];
99 }
100 return out;
101}
102
117template <std::meta::info Ent, std::meta::info SpecTmpl>
118consteval const char* annotation_text_of() {
119 template for (constexpr auto a :
120 std::define_static_array(std::meta::annotations_of(Ent))) {
121 constexpr std::meta::info t{std::meta::type_of(a)};
122 if constexpr (std::meta::has_template_arguments(t) &&
123 std::meta::template_of(t) == SpecTmpl) {
124 using spec_type = [:t:];
125 constexpr auto spec{std::meta::extract<spec_type>(a)};
126 return std::define_static_string(cleandoc(spec.text.data));
127 }
128 }
129 return nullptr;
130}
131
137template <std::meta::info Ent>
138consteval const char* doc_of() {
140}
141
147template <std::meta::info Fn>
148consteval const char* return_doc_of() {
150}
151
152namespace detail {
156 const char* name{nullptr};
157 const char* text{nullptr};
158};
159} // namespace detail
160
168template <std::meta::info Ent>
170 size_type n{0};
171 for (auto a : std::meta::annotations_of(Ent)) {
172 auto t{std::meta::type_of(a)};
173 if (std::meta::has_template_arguments(t) &&
174 std::meta::template_of(t) == ^^detail::tparam_spec)
175 ++n;
176 }
177 return n;
178}
179
192template <std::meta::info Ent>
193consteval auto tparam_docs() {
194 std::array<detail::tparam_doc, tparam_count<Ent>()> out{};
195 size_type i{0};
196 template for (constexpr auto a :
197 std::define_static_array(std::meta::annotations_of(Ent))) {
198 constexpr std::meta::info t{std::meta::type_of(a)};
199 if constexpr (std::meta::has_template_arguments(t) &&
200 std::meta::template_of(t) == ^^detail::tparam_spec) {
201 using spec_type = [:t:];
202 constexpr auto spec{std::meta::extract<spec_type>(a)};
203 out[i++] = detail::tparam_doc{std::define_static_string(spec.name.data),
204 std::define_static_string(spec.text.data)};
205 }
206 }
207 return out;
208}
209
210namespace detail {
213struct param_doc {
214 const char* name{nullptr};
215 const char* text{nullptr};
216};
217} // namespace detail
218
226template <std::meta::info Fn>
227consteval auto param_docs() {
228 constexpr size_type n{std::meta::parameters_of(Fn).size()};
229 std::array<detail::param_doc, n> out{};
230 // The final `i++` in the unrolled `template for` is a dead store (its result is
231 // never read), which trips -Wunused-but-set-variable; the counter is genuinely
232 // used to index `out`, so mark it maybe_unused rather than restructure.
233 [[maybe_unused]] size_type i{0};
234 template for (constexpr auto p :
235 std::define_static_array(std::meta::parameters_of(Fn))) {
236 const char* name{std::meta::has_identifier(p)
237 ? std::define_static_string(std::meta::identifier_of(p))
238 : nullptr};
239 out[i++] = detail::param_doc{name, doc_of<p>()};
240 }
241 return out;
242}
243
244// --- docstring styles -------------------------------------------------------
245
246namespace detail {
254 const char* summary{nullptr};
255 std::span<const param_doc> params{};
256 const char* returns{nullptr};
257};
258} // namespace detail
259
260// The `doc_style` concept — the customization point that folds a `function_doc`
261// into one docstring — lives in <welder/concepts.hpp> (with welder's other
262// interface concepts); `function_docstring` below constrains its `Style` on it.
263
275template <std::meta::info Fn, doc_style Style>
276std::string function_docstring() {
277 static constexpr auto pds{param_docs<Fn>()};
278 return Style::format(detail::function_doc{doc_of<Fn>(),
279 std::span<const detail::param_doc>{pds},
281}
282
283} // namespace welder
The core interface concepts welder's static polymorphism rests on, gathered in one catalogue: the cus...
consteval auto param_docs()
The parameter docs of function Fn, in declaration order.
Definition doc.hpp:227
consteval const char * annotation_text_of()
The text of the annotation on Ent whose class template is SpecTmpl, or nullptr if it carries none.
Definition doc.hpp:118
consteval std::string cleandoc(std::string_view text)
Normalize a docstring the way Python's inspect.cleandoc (PEP 257) does, so a multiline doc/returns ca...
Definition doc.hpp:45
consteval auto tparam_docs()
The template-parameter docs declared on Ent via [[=welder::tparam("T","…")]], in annotation order.
Definition doc.hpp:193
consteval const char * return_doc_of()
The returns text on function Fn (documentation of its return value), or nullptr.
Definition doc.hpp:148
std::string function_docstring()
The complete docstring for function Fn under Style.
Definition doc.hpp:276
consteval const char * doc_of()
The doc text on Ent (a class, namespace, function, or parameter), or nullptr.
Definition doc.hpp:138
consteval size_type tparam_count()
How many tparam annotations Ent carries.
Definition doc.hpp:169
decltype(sizeof(0)) size_type
std::size_t named without a standard-library include, keeping this vocabulary header std-free (see th...
The raw documentation pieces of a function, handed to a style to assemble.
Definition doc.hpp:253
std::span< const param_doc > params
Per-parameter doc, declaration order.
Definition doc.hpp:255
const char * returns
The function's returns.
Definition doc.hpp:256
const char * summary
The function's own doc.
Definition doc.hpp:254
One function parameter's documentation: its identifier (nullptr if unnamed) and its doc text (nullptr...
Definition doc.hpp:213
const char * name
The parameter's identifier, or nullptr.
Definition doc.hpp:214
const char * text
Its doc text, or nullptr.
Definition doc.hpp:215
One documented template parameter of an entity: the parameter's name (as spelled in the tparam annota...
Definition doc.hpp:155
const char * name
The template parameter's name.
Definition doc.hpp:156
const char * text
Its documentation.
Definition doc.hpp:157
The stored form of a tparam annotation: one template parameter's doc.