From a10e0321488ee9ada293002eeda1d8a57a6d63f7 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 1 Jun 2024 20:08:41 -0700 Subject: [PATCH] Improve docs --- doc/api.md | 33 ++--- include/fmt/format.h | 124 +++++++----------- support/mkdocstrings_handlers/cxx/__init__.py | 52 +++++--- 3 files changed, 95 insertions(+), 114 deletions(-) diff --git a/doc/api.md b/doc/api.md index fd3fc5ce..21b49b25 100644 --- a/doc/api.md +++ b/doc/api.md @@ -273,7 +273,7 @@ You can create your own formatting function with compile-time checks and small binary footprint, for example ([run](https://godbolt.org/z/b9Pbasvzc)): ```c++ -#include +#include void vlog(const char* file, int line, fmt::string_view fmt, fmt::format_args args) { @@ -514,14 +514,14 @@ feature](https://en.cppreference.com/w/cpp/feature_test). #include - std::variant v0{'x'}; - // Prints "variant('x')" - fmt::print("{}", v0); + fmt::print("{}", std::variant('x')); + // Output: variant('x') - std::variant v1; - // Prints "variant(monostate)" + fmt::print("{}", std::variant()); + // Output: variant(monostate) -## Format String Compilation {#compile-api} + +## Format String Compilation `fmt/compile.h` provides format string compilation enabled via the `FMT_COMPILE` macro or the `_cf` user-defined literal. Format strings @@ -615,26 +615,19 @@ if an argument type doesn\'t match its format specification. ::: sprintf(const S&, const T&...) -## `wchar_t` Support {#xchar-api} + +## Wide Strings The optional header `fmt/xchar.h` provides support for `wchar_t` and exotic character types. -:: {.doxygenstruct} -fmt::is_char -:: +::: is_char -:: {.doxygentypedef} -fmt::wstring_view -:: +::: wstring_view -:: {.doxygentypedef} -fmt::wformat_context -:: +::: wformat_context -:: {.doxygenfunction} -fmt::to_wstring(const T &value) -:: +::: to_wstring(const T&) ## Compatibility with C++20 `std::format` diff --git a/include/fmt/format.h b/include/fmt/format.h index 86126a53..8661bec2 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -813,25 +813,17 @@ FMT_BEGIN_EXPORT enum { inline_buffer_size = 500 }; /** - \rst - A dynamically growing memory buffer for trivially copyable/constructible types - with the first ``SIZE`` elements stored in the object itself. - - You can use the ``memory_buffer`` type alias for ``char`` instead. - - **Example**:: - - auto out = fmt::memory_buffer(); - fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); - - This will append the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42. - - The output can be converted to an ``std::string`` with ``to_string(out)``. - \endrst + * A dynamically growing memory buffer for trivially copyable/constructible + * types with the first `SIZE` elements stored in the object itself. Most + * commonly used via the `memory_buffer` alias for `char`. + * + * **Example**:: + * + * auto out = fmt::memory_buffer(); + * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); + * + * This will append "The answer is 42." to `out`. The buffer content can be + * converted to `std::string` with `to_string(out)`. */ template > @@ -904,22 +896,14 @@ class basic_memory_buffer : public detail::buffer { } public: - /** - \rst - Constructs a :class:`fmt::basic_memory_buffer` object moving the content - of the other object to it. - \endrst - */ + /// Constructs a `basic_memory_buffer` object moving the content of the other + /// object to it. FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept : detail::buffer(grow) { move(other); } - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ + /// Moves the content of the other `basic_memory_buffer` object to this one. auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); @@ -930,13 +914,11 @@ class basic_memory_buffer : public detail::buffer { // Returns a copy of the allocator associated with this buffer. auto get_allocator() const -> Allocator { return alloc_; } - /** - Resizes the buffer to contain *count* elements. If T is a POD type new - elements may not be initialized. - */ + /// Resizes the buffer to contain *count* elements. If T is a POD type new + /// elements may not be initialized. FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } - /** Increases the buffer capacity to *new_capacity*. */ + /// Increases the buffer capacity to *new_capacity*. void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } using detail::buffer::append; @@ -965,7 +947,7 @@ FMT_BEGIN_EXPORT # pragma clang diagnostic ignored "-Wweak-vtables" #endif -/** An error reported from a formatting function. */ +/// An error reported from a formatting function. class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; @@ -1828,14 +1810,12 @@ inline auto find_escape(const char* begin, const char* end) }() /** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst + * Constructs a compile-time format string from a string literal *s*. + * + * **Example**:: + * + * // A compile-time error because 'd' is an invalid specifier for strings. + * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); */ #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) @@ -4028,13 +4008,11 @@ template struct formatter : formatter, Char> {}; /** - \rst - Converts ``p`` to ``const void*`` for pointer formatting. - - **Example**:: - - auto s = fmt::format("{}", fmt::ptr(p)); - \endrst + * Converts `p` to `const void*` for pointer formatting. + * + * **Example**:: + * + * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { static_assert(std::is_pointer::value, ""); @@ -4042,14 +4020,12 @@ template auto ptr(T p) -> const void* { } /** - \rst - Converts ``e`` to the underlying type. - - **Example**:: - - enum class color { red, green, blue }; - auto s = fmt::format("{}", fmt::underlying(color::red)); - \endrst + * Converts `e` to the underlying type. + * + * **Example**:: + * + * enum class color { red, green, blue }; + * auto s = fmt::format("{}", fmt::underlying(color::red)); */ template constexpr auto underlying(Enum e) noexcept -> underlying_t { @@ -4199,15 +4175,11 @@ template struct nested_formatter { }; /** - \rst - Converts *value* to ``std::string`` using the default format for type *T*. - - **Example**:: - - #include - - std::string answer = fmt::to_string(42); - \endrst + * Converts *value* to ``std::string`` using the default format for type *T*. + * + * **Example**:: + * + * std::string answer = fmt::to_string(42); */ template ::value && !detail::has_format_as::value)> @@ -4381,15 +4353,13 @@ constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { FMT_API auto vformat(string_view fmt, format_args args) -> std::string; /** - \rst - Formats ``args`` according to specifications in ``fmt`` and returns the result - as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}.", 42); - \endrst + * Formats `args` according to specifications in `fmt` and returns the result + * as a string. + * + * **Example**:: + * + * #include + * std::string message = fmt::format("The answer is {}.", 42); */ template FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) diff --git a/support/mkdocstrings_handlers/cxx/__init__.py b/support/mkdocstrings_handlers/cxx/__init__.py index f7fe2a67..b6a111c8 100644 --- a/support/mkdocstrings_handlers/cxx/__init__.py +++ b/support/mkdocstrings_handlers/cxx/__init__.py @@ -52,7 +52,7 @@ def doxyxml2html(nodes: list[et.Element]): out += n.tail return out -def get_template_params(node: et.Element) -> Optional[list[Definition]]: +def convert_template_params(node: et.Element) -> Optional[list[Definition]]: templateparamlist = node.find('templateparamlist') if templateparamlist is None: return None @@ -81,10 +81,20 @@ def convert_type(type: et.Element) -> str: result += type.tail.strip() return clean_type(result) -def convert_param(param: et.Element) -> Definition: - d = Definition(param.find('declname').text, 'param') - d.type = convert_type(param.find('type')) - return d +def convert_params(func: et.Element) -> Definition: + params = [] + for p in func.findall('param'): + d = Definition(p.find('declname').text, 'param') + d.type = convert_type(p.find('type')) + params.append(d) + return params + +def convert_return_type(d: Definition, node: et.Element) -> None: + d.trailing_return_type = None + if d.type == 'auto' or d.type == 'constexpr auto': + parts = node.find('argsstring').text.split(' -> ') + if len(parts) > 1: + d.trailing_return_type = clean_type(parts[1]) def render_decl(d: Definition) -> None: text = '
'
@@ -105,7 +115,7 @@ def render_decl(d: Definition) -> None:
     text += '(' + escape_html(params) + ')'
     if d.trailing_return_type:
       text += '\n ' \
-        if len(d.name) + len(params) + len(d.trailing_return_type) > 74 else ''
+        if len(d.name) + len(params) + len(d.trailing_return_type) > 68 else ''
       text += ' -> ' + escape_html(d.trailing_return_type)
   elif d.kind == 'typedef':
     text += ' = ' + escape_html(d.type)
@@ -182,20 +192,16 @@ class CxxHandler(BaseHandler):
       params = None
       kind = node.get('kind')
       if kind == 'function':
-        params = [convert_param(p) for p in node.findall('param')]
+        params = convert_params(node)
         node_param_str = ', '.join([p.type for p in params])
         if param_str and param_str != node_param_str:
           candidates.append(f'{name}({node_param_str})')
           continue
       d = Definition(name, kind)
       d.type = convert_type(node.find('type'))
-      d.template_params = get_template_params(node)
+      d.template_params = convert_template_params(node)
       d.params = params
-      d.trailing_return_type = None
-      if d.type == 'auto' or d.type == 'constexpr auto':
-        parts = node.find('argsstring').text.split(' -> ')
-        if len(parts) > 1:
-          d.trailing_return_type = clean_type(parts[1])
+      convert_return_type(d, node)
       d.desc = get_description(node)
       return d
     
@@ -207,16 +213,28 @@ class CxxHandler(BaseHandler):
       xml = et.parse(f)
       node = xml.find('compounddef')
       d = Definition(name, node.get('kind'))
-      d.template_params = get_template_params(node)
+      d.template_params = convert_template_params(node)
       d.desc = get_description(node)
       d.members = []
-      for m in node.findall('sectiondef[@kind="public-attrib"]/memberdef'):
+      for m in node.findall('sectiondef[@kind="public-attrib"]/memberdef') + \
+               node.findall('sectiondef[@kind="public-func"]/memberdef'):
         name = m.find('name').text
-        member = Definition(name if name else '', m.get('kind'))
+        # Doxygen incorrectly classifies members of private unnamed unions as
+        # public members of the containing class.
+        if name.endswith('_'):
+          continue
+        desc = get_description(m)
+        if len(desc) == 0:
+          continue
+        kind = m.get('kind')
+        member = Definition(name if name else '', kind)
         type = m.find('type').text
         member.type = type if type else ''
+        if kind == 'function':
+          member.params = convert_params(m)
+          convert_return_type(member, m)
         member.template_params = None
-        member.desc = get_description(m)
+        member.desc = desc
         d.members.append(member)
       return d