diff --git a/doc/api.md b/doc/api.md index 6a1cee7c..659b0edb 100644 --- a/doc/api.md +++ b/doc/api.md @@ -275,7 +275,7 @@ Unused arguments are allowed as in Python's `str.format` and ordinary functions. Named arguments are not supported in compile-time checks at the moment. -### Argument Lists +### Type Erasure You can create your own formatting function with compile-time checks and small binary footprint, for example ([run](https://godbolt.org/z/b9Pbasvzc)): @@ -493,7 +493,8 @@ chrono-format-specifications). ::: gmtime(std::time_t) -## Standard Library Types Formatting {#std-api} + +## Standard Library Types Formatting `fmt/std.h` provides formatters for: @@ -550,13 +551,12 @@ Format string compilation can generate more binary code compared to the default API and is only recommended in places where formatting is a performance bottleneck. -:: {.doxygendefine} -FMT_COMPILE -:: +::: FMT_COMPILE ::: operator""_cf -## Terminal Color and Text Style {#color-api} + +## Terminal Color and Text Style `fmt/color.h` provides support for terminal color and text style output. @@ -568,13 +568,15 @@ FMT_COMPILE ::: styled(const T&, text_style) -## System APIs {#os-api} + +## System APIs ::: ostream ::: windows_error -## `std::ostream` Support {#ostream-api} + +## `std::ostream` Support `fmt/ostream.h` provides `std::ostream` support including formatting of user-defined types that have an overloaded insertion operator @@ -601,14 +603,16 @@ you should provide a `formatter` specialization inherited from ::: print(std::ostream&, format_string, T&&...) -## Dynamic Argument Lists {#args-api} + +## Dynamic Argument Lists The header `fmt/args.h` provides `dynamic_format_arg_store`, a builder-like API that can be used to construct format argument lists dynamically. ::: dynamic_format_arg_store -## `printf` Formatting {#printf-api} + +## `printf` Formatting The header `fmt/printf.h` provides `printf`-like formatting functionality. The following functions use [printf format string diff --git a/include/fmt/compile.h b/include/fmt/compile.h index f5872500..ae4b8be5 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -30,17 +30,15 @@ template struct is_compiled_string : std::is_base_of {}; /** - \rst - Converts a string literal *s* into a format string that will be parsed at - compile time and converted into efficient formatting code. Requires C++17 - ``constexpr if`` compiler support. - - **Example**:: - - // Converts 42 into std::string using the most efficient method and no - // runtime format string processing. - std::string s = fmt::format(FMT_COMPILE("{}"), 42); - \endrst + * Converts a string literal `s` into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires C++17 + * `constexpr if` compiler support. + * + * **Example**:: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * std::string s = fmt::format(FMT_COMPILE("{}"), 42); */ #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) # define FMT_COMPILE(s) \ diff --git a/support/mkdocstrings_handlers/cxx/__init__.py b/support/mkdocstrings_handlers/cxx/__init__.py index 8880e4c3..51f95006 100644 --- a/support/mkdocstrings_handlers/cxx/__init__.py +++ b/support/mkdocstrings_handlers/cxx/__init__.py @@ -73,6 +73,8 @@ def normalize_type(type: str) -> str: return type.replace(' &', '&').replace(' *', '*') def convert_type(type: et.Element) -> str: + if type is None: + return None result = type.text if type.text else '' for ref in type: result += ref.text @@ -98,20 +100,27 @@ def convert_return_type(d: Definition, node: et.Element) -> None: def render_decl(d: Definition) -> None: text = '
'
+
   if d.template_params is not None:
     text += 'template <'
     text += ', '.join(
       [f'{p.type} {p.name}'.rstrip() for p in d.template_params])
     text += '>\n'
+
+  end = ';'
   if d.kind == 'function' or d.kind == 'variable':
-    text += d.type
+    text += d.type + ' '
   elif d.kind == 'typedef':
-    text += 'using'
+    text += 'using '
+  elif d.kind == 'define':
+    end = ''
   else:
-    text += d.kind
-  text += ' ' + d.name
+    text += d.kind + ' '
+  text += d.name
+
   if d.params is not None:
-    params = ', '.join([f'{p.type} {p.name}' for p in d.params])
+    params = ', '.join([
+      (p.type + ' ' if p.type else '') + p.name for p in d.params])
     text += '(' + escape_html(params) + ')'
     if d.trailing_return_type:
       text += '\n ' \
@@ -119,7 +128,8 @@ def render_decl(d: Definition) -> None:
       text += ' -> ' + escape_html(d.trailing_return_type)
   elif d.kind == 'typedef':
     text += ' = ' + escape_html(d.type)
-  text += ';'
+
+  text += end
   text += '
\n' return text @@ -174,50 +184,12 @@ class CxxHandler(BaseHandler): if p.returncode != 0: raise CalledProcessError(p.returncode, cmd) - def collect(self, identifier: str, config: Mapping[str, Any]) -> Definition: - qual_name = 'fmt::' + identifier + with open(os.path.join(self._doxyxml_dir, 'compile_8h.xml')) as f: + self._file_doxyxml = et.parse(f) - param_str = None - paren = qual_name.find('(') - if paren > 0: - qual_name, param_str = qual_name[:paren], qual_name[paren + 1:-1] - - colons = qual_name.rfind('::') - namespace, name = qual_name[:colons], qual_name[colons + 2:] - - # Load XML. - doxyxml = self._ns2doxyxml.get(namespace) - if doxyxml is None: - path = f'namespace{namespace.replace("::", "_1_1")}.xml' - with open(os.path.join(self._doxyxml_dir, path)) as f: - doxyxml = et.parse(f) - self._ns2doxyxml[namespace] = doxyxml - - nodes = doxyxml.findall( - f"compounddef/sectiondef/memberdef/name[.='{name}']/..") - candidates = [] - for node in nodes: - # Process a function or a typedef. - params = None - kind = node.get('kind') - if kind == 'function': - 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 = convert_template_params(node) - d.params = params - convert_return_type(d, node) - d.desc = get_description(node) - return d - - # Process a compound definition such as a struct. - cls = doxyxml.findall(f"compounddef/innerclass[.='{qual_name}']") - if not cls: - raise Exception(f'Cannot find {identifier}. Candidates: {candidates}') + def collect_compound(self, identifier: str, + cls: list[et.Element]) -> Definition: + '''Collect a compound definition such as a struct.''' path = os.path.join(self._doxyxml_dir, cls[0].get('refid') + '.xml') with open(path) as f: xml = et.parse(f) @@ -248,6 +220,60 @@ class CxxHandler(BaseHandler): d.members.append(member) return d + def collect(self, identifier: str, config: Mapping[str, Any]) -> Definition: + qual_name = 'fmt::' + identifier + + param_str = None + paren = qual_name.find('(') + if paren > 0: + qual_name, param_str = qual_name[:paren], qual_name[paren + 1:-1] + + colons = qual_name.rfind('::') + namespace, name = qual_name[:colons], qual_name[colons + 2:] + + # Load XML. + doxyxml = self._ns2doxyxml.get(namespace) + if doxyxml is None: + path = f'namespace{namespace.replace("::", "_1_1")}.xml' + with open(os.path.join(self._doxyxml_dir, path)) as f: + doxyxml = et.parse(f) + self._ns2doxyxml[namespace] = doxyxml + + nodes = doxyxml.findall( + f"compounddef/sectiondef/memberdef/name[.='{name}']/..") + if len(nodes) == 0: + nodes = self._file_doxyxml.findall( + f"compounddef/sectiondef/memberdef/name[.='{name}']/..") + candidates = [] + for node in nodes: + # Process a function or a typedef. + params = None + kind = node.get('kind') + if kind == 'function': + 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 + elif kind == 'define': + params = [] + for p in node.findall('param'): + d = Definition(p.find('defname').text, 'param') + d.type = None + params.append(d) + d = Definition(name, kind) + d.type = convert_type(node.find('type')) + d.template_params = convert_template_params(node) + d.params = params + convert_return_type(d, node) + d.desc = get_description(node) + return d + + cls = doxyxml.findall(f"compounddef/innerclass[.='{qual_name}']") + if not cls: + raise Exception(f'Cannot find {identifier}. Candidates: {candidates}') + return self.collect_compound(identifier, cls) + def render(self, d: Definition, config: dict) -> str: text = '
\n' text += render_decl(d)