chiark / gitweb /
lib/: Pure C machinery for handling `keyword arguments' to functions.
[sod] / doc / runtime.tex
index 8b8e080141eb08fc4d387f868a1c220bfa2bba72..f6f0846685ccd9f1daeaf1c6da2e15a800b78f61 100644 (file)
@@ -29,6 +29,560 @@ This chapter describes the runtime support macros and functions provided by
 the Sod library.  The common structure of object instances and classes is
 described in \xref{ch:structures}.
 
+%%%--------------------------------------------------------------------------
+\section{Keyword argument support} \label{sec:runtime.keywords}
+
+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}.
+
+
+\subsection{Type definitions} \label{sec:sec:runtime.keywords.types}
+
+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; \- \\
+     \};}
+
+  The @|kwval| structure describes a keyword argument name/value pair.  The
+  @|kw| member points to the name, as a null-terminated string.  The @|val|
+  member always contains the \emph{address} of the value.  (This somewhat
+  inconvenient arrangement makes the size of a @|kwval| object independent of
+  the actual argument type.)
+\end{describe}
+
+\begin{describe}[struct kwtab]{type}
+    {struct kwtab \{ \\ \ind
+       const struct kwval *v; \\
+       size_t n; \- \\
+     \};}
+
+  The @|kwtab| structure describes a list of keyword arguments, represented
+  as a vector of @|kwval| structures.  The @|v| member points to the start of
+  the vector; the @|n| member contains the number of elements in the vector.
+\end{describe}
+
+
+\subsection{Calling functions with keyword arguments}
+\label{sec:runtime.keywords.calling}
+
+Functions which accept keyword arguments are ordinary C functions with
+variable-length argument tails.  Hence, they can be called using ordinary C
+(of the right kind) and all will be well.  However, argument lists must
+follow certain rules (which will be described in full below); failure to do
+this will result in \emph{undefined behaviour}.
+
+The header file provides integration with some C compilers in the form of
+macros which can be used to help the compiler diagnose errors in calls to
+keyword-accepting functions; but such support is rather limited at the
+moment.  Some additional macros are provided for use in calls to such
+functions, and it is recommended that, where possible, these are used.  In
+particular, it's all too easy to forget the trailing null terminator which
+marks the end of a list of keyword arguments.
+
+That said, the underlying machinery is presented first, and the convenience
+macros are described later.
+
+\subsubsection{Keyword argument mechanism}
+The argument tail, following the mandatory arguments, consists of a sequence
+of zero or more alternating keyword names, as pointers to null-terminated
+strings (with type @|const char~*|), and their argument values.  This
+sequence is finally terminated by a null pointer (again with type @|const
+char~*|) in place of a keyword name.
+
+Each function may define for itself which keyword names it accepts,
+and what types the corresponding argument values should have.
+There are also (currently) three special keyword names.
+\begin{description} \let\makelabel\code
+
+\item[kw.valist] This special keyword is followed by a pointer to a
+  variable-length argument tail cursor object, of type @|va_list~*|.  This
+  cursor object will be modified as the function extracts successive
+  arguments from the tail.  The argument tail should consist of alternating
+  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
+  argument tail may itself contain the special keywords.
+
+\item[kw.tab] This special keyword is followed by \emph{two} argument values:
+  a pointer to the base of a vector of @|kwval| structures, and the number of
+  elements in this vector (as a @|size_t|).  Each element of the vector
+  describes a single keyword argument: the @|kw| member points to the
+  keyword's name, and the @|val| member points to the value.
+
+  The vector may contain special keywords.  The @|val| pointer for a
+  @|kw.valist| argument should contain the address of an object of type
+  @|va_list~*| (and not point directly to the cursor object, since @|val| is
+  has type @|const void~*| but the cursor will be modified as its argument
+  tail is traversed).  The @|val| pointer for a @|kw.tab| argument should
+  contain the address of a @|kwtab| structure which itself contains the base
+  address and length of the argument vector to be processed.
+
+\item[kw.unknown] This keyword is never accepted by any function.  If it is
+  encountered, the @|kw_unknown| function is called to report the situation
+  as an error; see below.
+
+\end{description}
+It is possible to construct a circular structure of indirect argument lists
+(in a number of ways).  Don't try to pass such a structure to a function: the
+result will be unbounded recursion or some other bad outcome.
+
+\subsubsection{Argument list structuring macros}
+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>)}
+  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.
+\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.
+\end{describe}
+
+The following keyword-argument macros can be used within the @|KWARGS|
+@<body> argument.
+
+\begin{describe}[K]{mac}{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>)}
+  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>)}
+  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.
+\end{describe}
+
+
+\subsection{Defining functions with keyword arguments}
+\label{sec:runtime.keywords.defining}
+
+\subsubsection{Keyword sets}
+A \emph{keyword set} defines the collection of keyword arguments accepted by
+a particular function.  The same keyword set may be used by several
+functions.  (If your function currently accepts no keyword arguments, but you
+plan to add some later, do not define a keyword set, and use the
+@|KWPARSE_EMPTY| macro described below.)
+
+Each keyword set has a name, which is a C identifier.  It's good to choose
+meaningful and distinctive names for keyword sets.  Keyword set names are
+meaningful at runtime: they are used as part of the @|kw_unknown| protocol
+(\xref{sec:runtime.keywords.unknown}), and may be examined by handler
+functions, or reported to a user in error messages.  For a keyword set which
+is used only by a single function, it is recommended that the set be given
+the same name as the function.
+
+The keyword arguments for a keyword set named @<set> are described by a `list
+macro' named @|@<set>{}_KWSET|.  This macro takes a single argument,
+conventionally named @`_'.
+
+It should expand to a sequence of one or more list items of the form
+\begin{prog}
+  _(@<type>, @<name>, @<default>)
+\end{prog}
+with no separation between them.
+
+For example:
+\begin{prog}
+  \#define example_KWSET(_) @\\ \\ \ind
+    _(int, x, 0) @\\ \\
+    _(const char *, y, NULL)
+\end{prog}
+
+Each @<name> should be a distinct C identifier; they will be used to name
+structure members.  An argument @<name> should not end with the suffix
+@`_suppliedp' (for reasons which will soon become apparent).
+
+Each @<type> should be a C @<type-name> such that
+\begin{prog}
+  @<type> @<name> ;
+\end{prog}
+is a valid declaration: so it may consist of declaration specifiers and
+(possibly qualified) pointer declarator markers, but not array or function
+markers (since they would have to be placed after the @<name>).  This is the
+same requirement made by the standard \man{va_arg}{3} macro.
+
+Each @<default> should be an initializer expression or brace-enclosed list,
+suitable for use in an aggregate initializer for a variable with automatic
+storage duration.  (In C89, aggregate initializers may contain only constant
+expressions; this restriction was lifted in C99.)
+
+\subsubsection{Function declaration markers}
+The following marker macros are intended to be used in both declarations and
+definitions of functions which accept keyword arguments.
+
+\begin{describe}{mac}{KWTAIL}
+  The @|KWTAIL| is expected to be used at the end of function parameter type
+  list to indicate that the function accepts keyword arguments; if there are
+  preceding mandatory arguments then the @|KWTAIL| marker should be separated
+  from them with a comma @`,'.  (It is permitted for a function parameter
+  type list to contain only a @|KWTAIL| marker.)
+
+  Specifically, the macro declares a mandatory argument @|const char
+  *kwfirst_| (to collect the first keyword name), and a variable-length
+  argument tail.
+
+  The \descref{KWPARSE}[macro]{mac} assumes that the enclosing function's
+  argument list ends with a @|KWTAIL| marker.
+\end{describe}
+
+\begin{describe}{mac}{KWCALL}
+  The @|KWCALL| macro acts as a declaration specifier for functions which
+  accept keyword arguments.  Its effect is to arrange for the compiler to
+  check, as far as is possible, that calls to the function are well-formed
+  according to the keyword-argument rules.  The exact checking performed
+  depends on the compiler's abilities (and how well supported the compiler
+  is): it may check that every other argument is a string; it may check that
+  the list is terminated with a null pointer; it may not do anything at all.
+  Again, this marker should be included in a function's definition and in any
+  declarations.
+\end{describe}
+
+\subsubsection{Auxiliary definitions}
+The following macros define data types and functions used for collecting
+keyword arguments.
+
+\begin{describe}[KWSET_STRUCT]{mac}{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>
+  listed in the keyword set definition; the other is a 1-bit-wide bitfield of
+  type @|unsigned int| named @|@<name>{}_suppliedp|.
+\end{describe}
+
+\begin{describe}[KWDECL]{mac}
+    {@<declaration-specifiers> KWDECL(@<set>, @<kw>);}
+  The macro declares and initializes a keyword argument structure variable
+  named @<kw> for the named keyword @<set>.  The optional
+  @<declaration-specifiers> may provide additional storage-class, qualifiers,
+  or other declaration specifiers.  The @`_suppliedp' flags are initialized
+  to zero; the other members are initialized with the corresponding defaults
+  from the keyword-set definition.
+\end{describe}
+
+\begin{describe}[KWSET_PARSEFN]{mac}
+    {@<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>);
+  \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
+  name.\footnote{%
+    I don't recommend declaring parser functions @|inline|: parser functions
+    are somewhat large, and modern compilers are pretty good at figuring out
+    whether to inline static functions.} %
+
+  The function's behaviour is as follows.  It parses keyword arguments from a
+  variable-length argument tail, and/or a vector of @|kwval| structures.
+  When a keyword argument is recognized, for some keyword @<name>, the
+  keyword argument structure pointed to by @<kw> is updated: the flag
+  @|@<name>{}_suppliedp| is set to 1; and the argument value is stored (by
+  simple assignment) in the @<name> member.
+
+  Hence, if the @`_suppliedp' members are initialized to zero, the caller can
+  determine which keyword arguments were supplied.  It is not possible to
+  discover whether two or more arguments have the same keyword: in this case,
+  the value from the last such argument is left in the keyword argument
+  structure, and any values from earlier arguments are lost.  (For this
+  purpose, the argument vector @<v> is scanned \emph{after} the
+  variable-length argument tail captured in @<ap>.)
+
+  The variable-argument tail is read from the list described by @|* @<ap>|.
+  The argument tail is expected to consist of alternating keyword strings (as
+  ordinary null-terminated strings) and the corresponding values, terminated
+  by a null pointer of type @|const char~*| in place of a keyword; except
+  that the first keyword (or terminating null pointer, if no arguments are
+  provided) is expected to have been extracted already and provided as the
+  @<kwfirst> argument; the first argument retrieved using the @|va_list|
+  cursor object should then be the value corresponding to the keyword named
+  by @<kwfirst>.\footnote{%
+    This slightly unusual convention makes it possible for a function to
+    collect the first keyword as a separate mandatory argument, which is
+    essential if there are no other mandatory arguments.  It also means that
+    the compiler will emit a diagnostic if you attempt to call a function
+    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
+  extracts successive arguments from the tail.
+
+  The keyword vector is read from the vector of @|kwval| structures starting
+  at address @<v> and containing the following @<n> items.  If @<n> is zero
+  then @<v> need not be a valid pointer.
+
+  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.
+\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>);}
+  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
+  enclosing function's last mandatory argument, which must have type @|const
+  char~*|.
+
+  It calls the function @|@<set>{}_kwparse| with five arguments: the address
+  of the keyword argument structure @<kw>; 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 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
+  function @|@<set>{}_kwparse| has been defined using
+  \descref{KWSET_PARSEFN}{mac} 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>);}
+  The macro @|KWPARSE| (note the lack of underscore) combines
+  \descref{KWDECL}{mac} and \descref{KW_PARSE}{mac}.  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}.
+
+  The macro expands both to a variable declaration and a statement: in C89,
+  declarations must precede statements, so under C89 rules this macro must
+  appear exactly between the declarations at the head of a brace-enclosed
+  block (typically the function body) and the statements at the end.  This
+  restriction was lifted in C99, so the macro may appear anywhere in the
+  function body.  However, it is recommended that callers avoid taking
+  actions which might require cleanup before attempting to parse their
+  keyword arguments, since keyword argument parsing functions invoke the
+  @|kw_unknown| handler (\xref{sec:runtime.keywords.unknown}) if they
+  encounter an unknown keyword, and the calling function will not get a
+  chance to tidy up after itself if this happens.
+\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
+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
+  argument list for a function which accepts no keyword arguments.
+
+  It calls the \descref{kw_parseempty}[function]{fun} 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
+  argument; a null pointer; and the value zero (signifying an empty
+  keyword-argument vector).
+
+  The effect is to check that the argument tail contains no 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
+  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}.
+\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>);}
+  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
+  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}.
+\end{describe}
+
+\subsection{Function wrappers} \label{sec:runtime.keywords.wrappers}
+
+Most users will not need the hairy machinery involving argument vectors.
+Their main use is in defining \emph{wrapper functions}.  Suppose there is a
+function @<f> which accepts some keyword arguments, and we want to write a
+function @<g> which accepts the same keywords recognized by @<f> and some
+additional ones.  Unfortunately @<f> may behave differently depending on
+whether or not a particular keyword argument is supplied at all, but it's not
+possible to synthesize a valid @|va_list| other than by simply capturing a
+live argument tail, and it's not possible to decide at runtime whether or not
+to include some arguments in a function call.  It's still possible to write
+@<g>, by building a vector of keyword arguments, collected one-by-one
+depending on the corresponding @`_suppliedp' flags.
+
+A few macros are provided to make this task easier.
+
+\begin{describe}[KW_COUNT]{mac}{KW_COUNT(@<set>)}
+  Returns the number of keywords defined in a keyword set named @<set>.
+\end{describe}
+
+\begin{describe}[KW_COPY]{mac}
+    {KW_COPY(@<fromset>, @<toset>, @<kw>, @<v>, @<n>);}
+
+  The macro @|KW_COPY| populates a vector of @|kwval| structures from a
+  keyword-argument structure.
+
+  The @<fromset> and @<toset> arguments should be the names of keyword sets;
+  @<kw> should be an lvalue of type @|@<fromset>{}_kwargs|; @<v> should be
+  the base address of a sufficiently large vector of @|struct kwval| objects;
+  and @<n> should be an lvalue of some appropriate integer type.  The
+  @<toset> must be a subset of @<fromset>: i.e., for every keyword defined in
+  @<toset> there is a keyword defined in @<fromset> with the same name and
+  type.
+
+  Successive elements of @<v>, starting at index @<n>, are filled in to refer
+  to the keyword arguments defined in @<toset> whose @`_suppliedp' flag is
+  set in the argument structure pointed to by @<kw>; for each such argument,
+  a pointer to the keyword name is stored in the corresponding vector
+  element's @|kw| member, and a pointer to the argument value, held in the
+  keyword argument structure, is stored in the vector element's @|val|
+  member.
+
+  At the end of this, the index @<n> is advanced so as to contain the index
+  of the first unused element of @<v>.  Hence, at most @|KW_COUNT(@<toset>)|
+  elements of @<v> will be used.
+\end{describe}
+
+
+\subsection{Handling unknown-keyword errors}
+\label{sec:runtime.keywords.unknown}
+
+When parsing a variable-length argument tail, it is not possible to continue
+after encountering an unknown keyword name.  This is because it is necessary
+to know the (promoted) type of the following argument value in order to skip
+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})
+call @|kw_unknown|.
+
+\begin{describe}[kw_unknown]{fun}
+    {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.
+
+  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}
+    {typedef void kw_unkhookfn(const char *@<set>, const char *@<kw>);}
+
+  The @|kw_unkhookfn| type is the type of unknown-keyword handler functions.
+  A handler function is given two arguments, both of which are pointers to
+  null-terminated strings: @<set> is the name of the keyword set expected;
+  and @<kw> is the name of the offending unknown keyword.
+\end{describe}
+
+\begin{describe}[kw_unkhook]{var}{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
+    without writing (moderately complex) system-specific code which would be
+    out of place in this library.  The author's intention is that the hook
+    variable @|kw_unkhook| be `owned' by some external library which can make
+    its functionality available to client programs in a safer and more
+    convenient way.  On Unix-like platforms (including Cygwin) that library
+    will be (a later version of) \textbf{mLib}; other platforms will likely
+    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
+  appropriate, but should not return to its caller.
+
+  Initially, this variable points to the
+  \descref{kw_defunknown}[function]{fun}.
+\end{describe}
+
+\begin{describe}[kw_defunknown]{fun}
+    {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}.
+\end{describe}
+
+As an example of the kind of special effect which can be achieved using this
+hook, the following hacking answers whether a function recognizes a
+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; \\
+  KW_TEST(f, somefunc(1, "two", 3, KWARGS_TEST("shiny", 68.7))); \\
+  /\=* now f is nonzero if `somefunc' accepts the `shiny' keyword \+ \\
+   {}* (which we hope wants a double argument) \\
+   {}*/
+\end{prog}
+
 %%%--------------------------------------------------------------------------
 \section{Object system support} \label{sec:runtime.object}