X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/888d5e58c8cff289ff56a9282608c153b402191c..6213be581b6fd4836c19af2f7970ecead5f8ae97:/doc/runtime.tex?ds=inline

diff --git a/doc/runtime.tex b/doc/runtime.tex
index 0490763..83acd31 100644
--- a/doc/runtime.tex
+++ b/doc/runtime.tex
@@ -34,7 +34,8 @@ described in \xref{ch:structures}.
 
 This section describes the types, macros, and functions exposed in the
 @|<sod/keyword.h>| header file which provides support for defining and
-calling functions which make use of keyword arguments; see \xref{sec:concepts.keywords}.
+calling functions which make use of keyword arguments; see
+\xref{sec:concepts.keywords}.
 
 
 \subsection{Type definitions} \label{sec:sec:runtime.keywords.types}
@@ -42,10 +43,10 @@ calling functions which make use of keyword arguments; see \xref{sec:concepts.ke
 The header file defines two simple structure types, and a function type which
 will be described later.
 
-\begin{describe}[struct kwval]{type}
-    {struct kwval \{ \\ \ind
-       const char *kw; \\
-       const void *val; \- \\
+\begin{describe}{ty}[struct kwval]
+    {struct kwval \{                                            \\ \ind
+       const char *kw;                                          \\
+       const void *val;                                       \-\\
      \};}
 
   The @|kwval| structure describes a keyword argument name/value pair.  The
@@ -55,10 +56,10 @@ will be described later.
   the actual argument type.)
 \end{describe}
 
-\begin{describe}[struct kwtab]{type}
-    {struct kwtab \{ \\ \ind
-       const struct kwval *v; \\
-       size_t n; \- \\
+\begin{describe}{ty}[struct kwtab]
+    {struct kwtab \{                                            \\ \ind
+       const struct kwval *v;                                   \\
+       size_t n;                                              \-\\
      \};}
 
   The @|kwtab| structure describes a list of keyword arguments, represented
@@ -106,7 +107,7 @@ There are also (currently) three special keyword names.
   keyword names and argument values, as described above, including the first
   keyword name.  (This is therefore different from the convention used when
   calling keyword argument parser functions: see the description of the
-  \descref{KWSET_PARSEFN}[macro]{mac} for more details about these.)  The
+  \descref{mac}{KWSET_PARSEFN}[macro] for more details about these.)  The
   argument tail may itself contain the special keywords.
 
 \item[kw.tab] This special keyword is followed by \emph{two} argument values:
@@ -136,40 +137,59 @@ result will be unbounded recursion or some other bad outcome.
 The following macros are intended to help with constructing keyword argument
 lists.  Their use is not essential, but may help prevent errors.
 
-\begin{describe}[KWARGS]{mac}{KWARGS(@<body>)}
+\begin{describe}{mac}[KWARGS]{KWARGS(@<body>)}
   The @<body> encloses a sequence of keyword arguments expressed as calls to
   argument consists of a sequence of calls to the keyword-argument macros
   described below, one after another without any separation.
 
-  In C89, macro actual arguments are not permitted to be empty; if there are
-  no keyword arguments to provide, and you're using a C89 compiler, then use
-  @|NO_KWARGS| (below) instead.  If your compiler supports C99 or later, it's
-  fine to just write @|KWARGS()| instead.
+  If there are no keyword arguments, use @|NO_KWARGS| (below) instead.
 \end{describe}
 
 \begin{describe}{mac}{NO_KWARGS}
   A marker, to be written instead of a @|KWARGS| invocation, to indicate that
   no keyword arguments are to be passed to a function.
 
-  This is unnecessary with compilers which support C99 or later, since once
-  can use @|KWARGS()| with an empty @<body> argument.
+  Using this macro instead of @|KWARGS| if there are no arguments does two
+  things:
+  \begin{itemize}
+
+  \item C89 doesn't permit empty macro arguments for some reason, so
+    @|NO_KWARGS| is necessary when using a C89 compiler.
+
+  \item Omitting the null terminator is a common mistake, so @|<keyword.h>|
+    tries to get the compiler to warn if you miss it.  However, the
+    \descref{mac}{KWTAIL}[macro] introduces an extra real argument
+    @|kwfirst_|, because it's not possible to scan a variable-length argument
+    tail if there are no mandatory arguments.  If you use @|KWARGS()|, with
+    an empty argument list, then the null terminator is passed as @|kwfirst_|
+    and the variable-length tail ends up empty, which might trigger a
+    compiler warning about the missing terminator.  @|NO_KWARGS| passes
+    \emph{two} null terminators: a real one to indicate that there are no
+    keyword arguments, and a dummy one to placate the compiler.
+
+    (Sod method entry functions don't have this extra argument, so
+    @|KWARGS()| would work fine for sending Sod messages with keyword
+    arguments if you're using C99 or later, but it's probably best to use the
+    version which always works.)
+
+  \end{itemize}
 \end{describe}
 
 The following keyword-argument macros can be used within the @|KWARGS|
 @<body> argument.
 
-\begin{describe}[K]{mac}{K(@<name>, @<value>)}
+\begin{describe}{mac}[K]{K(@<name>, @<value>)}
   Passes a keyword @<name> and its corresponding @<value>, as a pair of
   arguments.  The @<name> should be a single identifier (not a quoted
   string).  The @<value> may be any C expression of the appropriate type.
 \end{describe}
 
-\begin{describe}[K_VALIST]{mac}{K_VALIST(@<ap>)}
+\begin{describe}{mac}[K_VALIST]{K_VALIST(@<ap>)}
   Passes an indirect variable-length argument tail.  The argument @<ap>
   should be an lvalue of type @|va_list|, which will be passed by reference.
 \end{describe}
 
-\begin{describe}[K_TAB]{mac}{K_TAB(@<v>, @<n>)}
+\begin{describe}{mac}[K_TAB]{K_TAB(@<v>, @<n>)}
   Passes a vector of keyword arguments.  The argument @<v> should be the base
   address of the vector, and @<n> should be the number of elements in the
   vector.
@@ -203,8 +223,8 @@ list items of the form
 \end{prog}
 with no separation between them.  For example:
 \begin{prog}
-  \#define example_KWSET(_) @\\ \\ \ind
-    _(int, x, 0) @\\ \\
+  \#define example_KWSET(_)                             \macsl  \\ \ind
+    _(int, x, 0)                                        \macsl  \\
     _(const char *, y, NULL)
 \end{prog}
 
@@ -241,7 +261,7 @@ definitions of functions which accept keyword arguments.
   *kwfirst_| (to collect the first keyword name), and a variable-length
   argument tail.
 
-  The \descref{KWPARSE}[macro]{mac} assumes that the enclosing function's
+  The \descref{mac}{KWPARSE}[macro] assumes that the enclosing function's
   argument list ends with a @|KWTAIL| marker.
 \end{describe}
 
@@ -261,7 +281,7 @@ definitions of functions which accept keyword arguments.
 The following macros define data types and functions used for collecting
 keyword arguments.
 
-\begin{describe}[KWSET_STRUCT]{mac}{KWSET_STRUCT(@<set>);}
+\begin{describe}{mac}[KWSET_STRUCT]{KWSET_STRUCT(@<set>);}
   The @|KWSET_STRUCT| macro defines a \emph{keyword structure} named @|struct
   @<set>{}_kwargs|.  For each argument defined in the keyword set, this
   structure contains two members: one has exactly the @<name> and @<type>
@@ -269,7 +289,7 @@ keyword arguments.
   type @|unsigned int| named @|@<name>{}_suppliedp|.
 \end{describe}
 
-\begin{describe}[KWDECL]{mac}
+\begin{describe}{mac}[KWDECL]
     {@<declaration-specifiers> KWDECL(@<set>, @<kw>);}
   The macro declares and initializes a keyword argument structure variable
   named @<kw> for the named keyword @<set>.  The optional
@@ -279,15 +299,16 @@ keyword arguments.
   corresponding defaults from the keyword-set definition.
 \end{describe}
 
-\begin{describe}[KWSET_PARSEFN]{mac}
+\begin{describe}{mac}[KWSET_PARSEFN]
     {@<declaration-specifiers> KWSET_PARSEFN(@<set>)}
 
   The macro @|KWSET_PARSEFN| defines a keyword argument \emph{parser
   function}
   \begin{prog}
-    void @<set>{}_kwparse(\=struct @<set>{}_kwargs *@<kw>,
-                            const char *@<kwfirst>, va_list *@<ap>, \+ \\
-                            const struct kwval *@<v>, size_t @<n>);
+    void @<set>{}_kwparse%
+      (\=struct @<set>{}_kwargs *@<kw>,
+         const char *@<kwfirst>, va_list *@<ap>,              \+\\
+         const struct kwval *@<v>, size_t @<n>);
   \end{prog}
   The macro call can (and usually will) be preceded by storage class
   specifiers such as @|static|, for example to adjust the linkage of the
@@ -327,7 +348,7 @@ keyword arguments.
     which expects keyword arguments, but don't supply any and forget the null
     pointer which terminates the (empty) list.} %
   If @<kwfirst> is a null pointer, then @<ap> need not be a valid pointer;
-  otherwise, the cursor object @|* @<ap>| will be modified as the function
+  otherwise, the cursor object @|*@<ap>| will be modified as the function
   extracts successive arguments from the tail.
 
   The keyword vector is read from the vector of @|kwval| structures starting
@@ -336,14 +357,14 @@ keyword arguments.
 
   The function also handles the special @|kw.valist| and @|kw.tab| arguments
   described above (\xref{sec:runtime.keywords.calling}).  If an unrecognized
-  keyword argument is encountered, then \descref{kw_unknown}{fun} is called.
+  keyword argument is encountered, then \descref{fun}{kw_unknown} is called.
 \end{describe}
 
 \subsubsection{Parsing keywords}
 The following macros make use of the definitions described above to actually
 make a function's keyword arguments available to it.
 
-\begin{describe}[KW_PARSE]{mac}{KW_PARSE(@<set>, @<kw>, @<kwfirst>);}
+\begin{describe}{mac}[KW_PARSE]{KW_PARSE(@<set>, @<kw>, @<kwfirst>);}
   The @|KW_PARSE| macro invokes a keyword argument parsing function.  The
   @<set> argument should name a keyword set; @<kw> should be an lvalue of
   type @|struct @<set>{}_kwargs|; and @<kwfirst> should be the name of the
@@ -357,21 +378,21 @@ make a function's keyword arguments available to it.
   final keyword argument; a null pointer; and the value zero (signifying an
   empty keyword-argument vector).
 
-  If the variable @<kw> was declared using \descref{KWDECL}{mac} and the
+  If the variable @<kw> was declared using \descref{mac}{KWDECL} and the
   function @|@<set>{}_kwparse| has been defined using
-  \descref{KWSET_PARSEFN}{mac} then the effect is to parse the keyword
+  \descref{mac}{KWSET_PARSEFN} then the effect is to parse the keyword
   arguments passed to the function and set the members of @<kw>
   appropriately.
 \end{describe}
 
-\begin{describe}[KWPARSE]{mac}{KWPARSE(@<set>);}
+\begin{describe}{mac}[KWPARSE]{KWPARSE(@<set>);}
   The macro @|KWPARSE| (note the lack of underscore) combines
-  \descref{KWDECL}{mac} and \descref{KW_PARSE}{mac}.  It declares and
+  \descref{mac}{KWDECL} and \descref{mac}{KW_PARSE}.  It declares and
   initializes a keyword argument structure variable with the fixed name
   @|kw|, and parses the keyword arguments provided to the enclosing function,
   storing the results in @|kw|.  It assumes that the first keyword name is in
   an argument named @|kwfirst_|, as set up by the
-  \descref{KWTAIL}[marker]{mac}.
+  \descref{mac}{KWTAIL}[marker].
 
   The macro expands both to a variable declaration and a statement: in C89,
   declarations must precede statements, so under C89 rules this macro must
@@ -387,18 +408,18 @@ make a function's keyword arguments available to it.
 \end{describe}
 
 As mentioned above, it is not permitted to define an empty keyword set.
-(Specifically, invoking \descref{KWSET_STRUCT}{mac} for an empty keyword set
+(Specifically, invoking \descref{mac}{KWSET_STRUCT} for an empty keyword set
 would result in attempting to define a structure with no members, which C
 doesn't allow.)  On the other hand, keyword arguments are a useful extension
 mechanism, and it's useful to be able to define a function which doesn't
 currently accept any keywords, but which might in the future be extended to
 allow keyword arguments.
 
-\begin{describe}[KW_PARSE_EMPTY]{mac}{KW_PARSE_EMPTY(@<set>, @<kwfirst>);}
-  This is an analogue to \descref{KW_PARSE}{mac} which checks the keyword
+\begin{describe}{mac}[KW_PARSE_EMPTY]{KW_PARSE_EMPTY(@<set>, @<kwfirst>);}
+  This is an analogue to \descref{mac}{KW_PARSE} which checks the keyword
   argument list for a function which accepts no keyword arguments.
 
-  It calls the \descref{kw_parseempty}[function]{fun} with five arguments:
+  It calls the \descref{fun}{kw_parseempty}[function] with five arguments:
   the @<set> name, as a string; the string pointer @<kwfirst>; the address of
   a temporary argument-tail cursor object of type @|va_list|, constructed on
   the assumption that @<kwfirst> is the enclosing function's final keyword
@@ -409,26 +430,27 @@ allow keyword arguments.
   other than the special predefined ones.
 \end{describe}
 
-\begin{describe}[KWPARSE_EMPTY]{mac}{KWPARSE_EMPTY(@<set>);}
-  This is an analogue to \descref{KWPARSE}{mac} which checks that the
+\begin{describe}{mac}[KWPARSE_EMPTY]{KWPARSE_EMPTY(@<set>);}
+  This is an analogue to \descref{mac}{KWPARSE} which checks that the
   enclosing function has been passed no keyword arguments other than the
   special predefined ones.  It assumes that the first keyword name is in an
-  argument named @|kwfirst_|, as set up by the \descref{KWTAIL}[marker]{mac}.
+  argument named @|kwfirst_|, as set up by the \descref{mac}{KWTAIL}[marker].
 \end{describe}
 
-\begin{describe}[kw_parseempty]{fun}
-    {void kw_parseempty(\=const char *@<set>,
-                          const char *@<kwfirst>, va_list *@<ap>, \+ \\
-                          const struct kwval *@<v>, size_t @<n>);}
+\begin{describe}{fun}[kw_parseempty]
+    {void kw_parseempty%
+      (\=const char *@<set>,
+         const char *@<kwfirst>, va_list *@<ap>,              \+\\
+         const struct kwval *@<v>, size_t @<n>);}
   This function checks an keyword argument list to make sure that contains no
   keyword arguments (other than the special ones described in
   \xref{sec:runtime.keywords.calling}).
 
   The @<set> argument should point to a null-terminated string: this will be
-  reported as the keyword set name to \descref{kw_unknown}{fun}, though it
+  reported as the keyword set name to \descref{fun}{kw_unknown}, though it
   need not (and likely will not) refer to any defined keyword set.  The
   remaining arguments are as for the keyword parsing functions defined by the
-  \descref{KWSET_PARSEFN}[macro]{mac}.
+  \descref{mac}{KWSET_PARSEFN}[macro].
 \end{describe}
 
 \subsection{Function wrappers} \label{sec:runtime.keywords.wrappers}
@@ -447,11 +469,11 @@ depending on the corresponding @`_suppliedp' flags.
 
 A few macros are provided to make this task easier.
 
-\begin{describe}[KW_COUNT]{mac}{KW_COUNT(@<set>)}
+\begin{describe}{mac}[KW_COUNT]{KW_COUNT(@<set>)}
   Returns the number of keywords defined in a keyword set named @<set>.
 \end{describe}
 
-\begin{describe}[KW_COPY]{mac}
+\begin{describe}{mac}[KW_COPY]
     {KW_COPY(@<fromset>, @<toset>, @<kw>, @<v>, @<n>);}
 
   The macro @|KW_COPY| populates a vector of @|kwval| structures from a
@@ -489,24 +511,24 @@ past it; but the only clue provided as to the type is the keyword name, which
 in this case is meaningless.
 
 In this situation, the parser functions generated by
-\descref{KWSET_PARSEFN}{mac} (and the \descref{kw_parseempty}[function]{fun})
+\descref{mac}{KWSET_PARSEFN} (and the \descref{fun}{kw_parseempty}[function])
 call @|kw_unknown|.
 
-\begin{describe}[kw_unknown]{fun}
+\begin{describe}{fun}[kw_unknown]
     {void kw_unknown(const char *@<set>, const char *@<kw>);}
 
   This is a function of two arguments: @<set> points to the name of the
   keyword set expected by the caller, as a null-terminated string; and @<kw>
   is the unknown keyword which was encountered.  All that @|kw_unknown| does
   is invoke the function whose address is stored in the global variable
-  \descref{kw_unkhook}{var} with the same arguments.
+  \descref{var}{kw_unkhook} with the same arguments.
 
   This function never returns to its caller: if the @|kw_unkhook| function
   returns (which it shouldn't) then @|kw_unknown| writes a fatal error
   message to the standard error stream and calls \man{abort}{3}.
 \end{describe}
 
-\begin{describe}[kw_unkhookfn]{type}
+\begin{describe}{ty}[kw_unkhookfn]
     {typedef void kw_unkhookfn(const char *@<set>, const char *@<kw>);}
 
   The @|kw_unkhookfn| type is the type of unknown-keyword handler functions.
@@ -515,7 +537,7 @@ call @|kw_unknown|.
   and @<kw> is the name of the offending unknown keyword.
 \end{describe}
 
-\begin{describe}[kw_unkhook]{var}{kw_unkhookfn *kw_unkhook}
+\begin{describe}{var}[kw_unkhook]{kw_unkhookfn *kw_unkhook}
   This variable\footnote{%
     Having a single global hook variable is obviously inadequate for a modern
     library, but dealing with multiple threads isn't currently possible
@@ -528,21 +550,21 @@ call @|kw_unknown|.
     need different arrangements.  The author is willing to coordinate any
     such efforts.} %
   holds the current unknown-keyword handler function.  It will be invoked by
-  \descref{kw_unknown}{fun}.  The function may take whatever action seems
+  \descref{fun}{kw_unknown}.  The function may take whatever action seems
   appropriate, but should not return to its caller.
 
   Initially, this variable points to the
-  \descref{kw_defunknown}[function]{fun}.
+  \descref{fun}{kw_defunknown}[function].
 \end{describe}
 
-\begin{describe}[kw_defunknown]{fun}
+\begin{describe}{fun}[kw_defunknown]
     {void kw_defunknown(const char *@<set>, const char *@<kw>);}
   This function simply writes a message to standard error, to the effect that
   the keyword named by @<kw> is not known in the keyword set @<set>, and
   calls \man{abort}{3}.
 
-  This function is the default value of the \descref{kw_unkhook}[hook
-  variable]{var}.
+  This function is the default value of the \descref{var}{kw_unkhook}[hook
+  variable].
 \end{describe}
 
 As an example of the kind of special effect which can be achieved using this
@@ -551,33 +573,33 @@ particular keyword argument.
 
 \begin{prog}
   \#define KWARGS_TEST(k, val) KWARGS(K(k, val) K(kw.unknown, 0))
-  \\+
+                                                                \\+
   static jmp_buf kw_test_jmp;
-  \\+
-  static void kw_test_unknown(const char *set, const char *kw) \\
-  \{ \\ \ind
-    if (strcmp(kw, "kw.unknown")) longjmp(kw_test_jmp, 1); \\
-    else longjmp(kw_test_jmp, 2); \- \\
-  \}
-  \\+
-  \#define KW_TEST(flag, set, call) do \{ @\\ \\ \ind
-    kw_unkhookfn *oldunk = kw_unkhook; @\\ \\
-    kw_unkhook = kw_test_unknown; @\\ \\
-    switch (setjmp(kw_test_jmp)) \{ @\\ \\ \ind
-      case 0: call; abort(); @\\ \\
-      case 1: flag = 1; break; @\\ \\
-      case 2: flag = 0; break; @\\ \\
-      default: abort(); \- @\\ \\
-    \} @\\ \\
-    kw_unkhook = oldunk; \- @\\ \\
-  \} while (0)
-  \\+
-  @/* Example of use */ \\
-  int f; \\
+                                                                \\+
+  static void kw_test_unknown(const char *set, const char *kw)  \\
+  \{                                                            \\ \ind
+    if (strcmp(kw, "kw.unknown")) longjmp(kw_test_jmp, 1);      \\
+    else longjmp(kw_test_jmp, 2);                             \-\\
+  \}                                                            \\+
+
+  \#define KW_TEST(flag, set, call) do \{               \macsl  \\ \ind
+    kw_unkhookfn *oldunk = kw_unkhook;                  \macsl  \\
+    kw_unkhook = kw_test_unknown;                       \macsl  \\
+    switch (setjmp(kw_test_jmp)) \{                     \macsl  \\ \ind
+      case 0: call; abort();                            \macsl  \\
+      case 1: flag = 1; break;                          \macsl  \\
+      case 2: flag = 0; break;                          \macsl  \\
+      default: abort();                                 \macsl\-\\
+    \}                                                  \macsl  \\
+    kw_unkhook = oldunk;                                \macsl\-\\
+  \} while (0)                                                  \\+
+
+  @/* Example of use */                                         \\
+  int f;                                                        \\
   KW_TEST(f, somefunc(1, "two", 3, KWARGS_TEST("shiny", 68.7))); \\
   /\=* \comment{now @|f| is nonzero if @|somefunc| accepts the
-        @|shiny| keyword} \+ \\
-   {}* \comment{(which we hope wants a @|double| argument)} \\
+        @|shiny| keyword}                                     \+\\
+   {}* \comment{(which we hope wants a @|double| argument)}     \\
    {}*/
 \end{prog}
 
@@ -593,6 +615,13 @@ instances and classes inherit from the standard @|SodObject| root object.
 While the translator can (at some effort) support alternative roots, they
 will require different run-time support machinery.
 
+\begin{describe}{feat}{SOD_RECKLESS}
+  Some of Sod's macros include runtime checking by default.  This checking
+  can be disabled if you value performance more than early diagnosis of
+  problems.  Define @|SOD_RECKLESS| to a nonzero value before including
+  @|<sod/sod.h>| to inhibit the runtime checking.
+\end{describe}
+
 
 \subsection{Layout utilities} \label{sec:runtime.object.layout}
 
@@ -606,7 +635,7 @@ translator.  Others may find them useful for special effects, but they can be
 tricky to understand and use correctly and can't really be recommended for
 general use.
 
-\begin{describe}[SOD_OFFSETDIFF]{mac}
+\begin{describe}{mac}[SOD_OFFSETDIFF]
     {ptrdiff_t SOD_OFFSETDIFF(@<type>, @<member>_1, @<member>_2);}
   Returns the signed offset between two members of a structure or union type.
 
@@ -619,7 +648,7 @@ general use.
   to be very useful elsewhere.
 \end{describe}
 
-\begin{describe}[SOD_ILAYOUT]{mac}
+\begin{describe}{mac}[SOD_ILAYOUT]
     {@<cls>{}__ilayout *SOD_ILAYOUT(@<cls>, @<chead>, const void *@<obj>);}
   Recovers the instance layout base address from a pointer to one of its
   instance chains.
@@ -637,7 +666,7 @@ general use.
   @|SOD_INSTBASE| macro (described below) is more suited to general use.
 \end{describe}
 
-\begin{describe}[SOD_INSTBASE]{mac}{void *SOD_INSTBASE(const @<cls> *@<obj>)}
+\begin{describe}{mac}[SOD_INSTBASE]{void *SOD_INSTBASE(const @<cls> *@<obj>)}
   Finds the base address of an instance's layout.
 
   Given a pointer @<obj> to an instance, @|SOD_INSTBASE| returns the base
@@ -655,7 +684,7 @@ general use.
 The following macros and functions query the runtime relationships between
 instances and classes.
 
-\begin{describe}[SOD_CLASSOF]{mac}
+\begin{describe}{mac}[SOD_CLASSOF]
     {const SodClass *SOD_CLASSOF(const @<cls> *@<obj>);}
   Returns the class object describing an instance's dynamic class.
 
@@ -665,7 +694,7 @@ instances and classes.
   for @<cls> itself, it's called @|@<cls>{}__class|.)
 \end{describe}
 
-\begin{describe}[sod_subclassp]{fun}
+\begin{describe}{fun}[sod_subclassp]
     {int sod_subclassp(const SodClass *@<sub>, const SodClass *@<super>);}
 
   Decide whether one class @<sub> is actually a subclass of another class
@@ -685,8 +714,8 @@ The following macros and functions are used to convert instance pointers of
 some (static) type into instance pointers of other static types to the same
 instance.
 
-\begin{describe}[SOD_XCHAIN]{mac}
-    {void *SOD_CHAIN(@<chead>, const @<cls> *@<obj>);}
+\begin{describe}{mac}[SOD_XCHAIN]
+    {void *SOD_XCHAIN(@<chead>, const @<cls> *@<obj>);}
   Performs a `cross-chain upcast'.
 
   Given a pointer @<obj> to an instance of a class of type @<cls> and the
@@ -700,9 +729,9 @@ instance.
 \end{describe}
 
 \begin{describe*}
-    {\dhead[SOD_CONVERT]{mac}
+    {\dhead{mac}[SOD_CONVERT]
        {@<cls> *SOD_CONVERT(@<cls>, const void *@<obj>);}
-     \dhead[sod_convert]{fun}
+     \dhead{fun}[sod_convert]
        {void *sod_convert(const SodClass *@<cls>, const void *@<obj>);}}
   Perform general conversions (up-, down-, and cross-casts) on instance
   pointers.
@@ -739,11 +768,11 @@ allocation strategies.  They don't concern themselves with allocation or
 deallocation, and applications are free to use any suitable mechanism.
 
 \begin{describe*}
-    {\dhead[SOD_INIT]{mac}
+    {\dhead{mac}[SOD_INIT]
        {@<cls> *SOD_INIT(@<cls>, void *@<p>, @<keywords>);}
-     \dhead[sod_init]{fun}
+     \dhead{fun}[sod_init]
        {void *sod_init(const SodClass *@<cls>, void *@<p>, \dots);}
-     \dhead[sod_initv]{fun}
+     \dhead{fun}[sod_initv]
        {void *sod_initv(const SodClass *@<cls>, void *@<p>, va_list @<ap>);}}
   Imprints and initializes an instance of a class @<cls> in the storage
   starting at address~@<p>.
@@ -753,7 +782,7 @@ deallocation, and applications are free to use any suitable mechanism.
 
   Keyword arguments for the initialization message may be provided.  The
   @|SOD_INIT| macro expects a single preprocessor-time argument which is
-  a use of one of \descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}; the
+  a use of one of \descref*{mac}{KWARGS} or \descref{mac}{NO_KWARGS}; the
   @|sod_init| function expects the keywords as a variable-length argument
   tail; and @|sod_initv| expects the keywords to be passed indirectly,
   through the captured argument-tail cursor @<ap>.
@@ -765,19 +794,19 @@ deallocation, and applications are free to use any suitable mechanism.
   \xref{sec:structures.layout.instance}.
 \end{describe*}
 
-\begin{describe}[sod_teardown]{fun}{int sod_teardown(void *@<p>);}
+\begin{describe}{fun}[sod_teardown]{int sod_teardown(void *@<p>);}
   Tears down an instance of a class, releasing any resources it holds.
 
-  This function is a very thin wrapper around sending the @|obj.teardown|
-  message.  See the description of that message
-  (page~\pageref{msg:obj.teardown}) and \xref{sec:concepts.lifecycle.death}
+  This function is a very thin wrapper around sending the
+  \descref*{msg}{obj.teardown} message.  See the description of that message
+  (\autopageref{msg:obj.teardown}) and \xref{sec:concepts.lifecycle.death}
   for details.
 \end{describe}
 
 \subsubsection{Automatic storage duration}
 The following macro constructs an instance with automatic storage duration.
 
-\begin{describe}[SOD_DECL]{mac}{SOD_DECL(@<cls>, @<var>, @<keywords>);}
+\begin{describe}{mac}[SOD_DECL]{SOD_DECL(@<cls>, @<var>, @<keywords>);}
   Declares and initializes an instance with automatic storage duration.
 
   Given a class name @<cls> and an identifier @<var>, @|SOD_DECL| declares
@@ -788,12 +817,17 @@ The following macro constructs an instance with automatic storage duration.
 
   Keyword arguments for the initialization message may be provided.  The
   macro expects a single preprocessor-time argument which is a use of one of
-  \descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}.
+  \descref*{mac}{KWARGS} or \descref{mac}{NO_KWARGS}.
 
   The instance has automatic storage duration: pointers to it will become
   invalid when control exits the scope of the declaration.  If necessary, the
   instance should be torn down before this happens, using the
-  \descref{sod_teardown}[function]{fun}.
+  \descref{fun}{sod_teardown}[function].  It may be appropriate to @|assert|
+  that the object is ready for deallocation at this time.
+
+  By default, this macro will abort the program if the size allocated for the
+  instance doesn't match the size required by the class object; set
+  \descref{feat}{SOD_RECKLESS} to inhibit this check.
 \end{describe}
 
 \subsubsection{Dynamic allocation}
@@ -802,9 +836,9 @@ standard C heap.  They don't work in freestanding implementations where
 @|malloc| and @|free| are not available.
 
 \begin{describe*}
-    {\dhead[SOD_MAKE]{mac}{@<cls> *SOD_MAKE(@<cls>, @<keywords>);}
-     \dhead[sod_make]{fun}{void *sod_make(const SodClass *@<cls>, \dots);}
-     \dhead[sod_makev]{fun}
+    {\dhead{mac}[SOD_MAKE]{@<cls> *SOD_MAKE(@<cls>, @<keywords>);}
+     \dhead{fun}[sod_make]{void *sod_make(const SodClass *@<cls>, \dots);}
+     \dhead{fun}[sod_makev]
        {void *sod_makev(const SodClass *@<cls>, va_list @<ap>);}}
   Constructs and returns a pointer to a new instance of @<cls>.
 
@@ -813,7 +847,7 @@ standard C heap.  They don't work in freestanding implementations where
 
   Keyword arguments for the initialization message may be provided.  The
   @|SOD_MAKE| macro expects a single preprocessor-time argument which is
-  a use of one of \descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}; the
+  a use of one of \descref*{mac}{KWARGS} or \descref{mac}{NO_KWARGS}; the
   @|sod_make| function expects the keywords as a variable-length argument
   tail; and @|sod_makev| expects the keywords to be passed indirectly,
   through the captured argument-tail cursor @<ap>.
@@ -821,19 +855,19 @@ standard C heap.  They don't work in freestanding implementations where
   Storage for the new instance will have been allocated using the standard
   @|malloc| function.  The easiest way to destroy the instance, when it is no
   longer needed, is probably to call the
-  \descref{sod_destroy}[function]{fun}.
+  \descref{fun}{sod_destroy}[function].
 
   The return value is an instance pointer for the class @<cls>; the
   @|SOD_MAKE| macro will have converted it to the correct type, so it should
   probably be used where possible.
 \end{describe*}
 
-\begin{describe}[sod_destroy]{fun}{int sod_destroy(void *@<p>);}
+\begin{describe}{fun}[sod_destroy]{int sod_destroy(void *@<p>);}
   Tears down and frees an instance allocated using @|malloc|.
 
   The pointer @<p> should be an instance pointer, i.e., a pointer to any of
   an instance's chains.  The instance is torn down, by sending it the
-  \descref{obj.teardown}[message]{msg}.  If the instance reports itself ready
+  \descref{msg}{obj.teardown}[message].  If the instance reports itself ready
   for deallocation, then its storage is released using @|free|.  The return
   value is the value returned by the @|obj.teardown| message.
 \end{describe}
