/usr/lib/*/libsod.la
/usr/lib/*/libsod.so
/usr/lib/*/pkgconfig
+/usr/share/man/man3/keyword.3
/usr/share/man/man3/sod.3
/usr/share/man/man3/sod-struct.3
@|MyClass~*| to a @|SuperClass~*|. See
\xref{sec:structures.layout.additional} for the formal description.
+%%%--------------------------------------------------------------------------
+\section{Keyword arguments} \label{sec:concepts.keywords}
+
+In standard C, the actual arguments provided to a function are matched up
+with the formal arguments given in the function definition according to their
+ordering in a list. Unless the (rather cumbersome) machinery for dealing
+with variable-length argument tails (@|<stdarg.h>|) is used, exactly the
+correct number of arguments must be supplied, and in the correct order.
+
+A \emph{keyword argument} is matched by its distinctive \emph{name}, rather
+than by its position in a list. Keyword arguments may be \emph{omitted},
+causing some default behaviour by the function. A function can detect
+whether a particular keyword argument was supplied: so the default behaviour
+need not be the same as that caused by any specific value of the argument.
+
+Keyword arguments can be provided in three ways.
+\begin{enumerate}
+\item Directly, as a variable-length argument tail, consisting (for the most
+ part) of alternating keyword names, as pointers to null-terminated strings,
+ and argument values, and terminated by a null pointer. This is somewhat
+ error-prone, and the support library defines some macros which help ensure
+ that keyword argument lists are well formed.
+\item Indirectly, through a @|va_list| object capturing a variable-length
+ argument tail passed to some other function. Such indirect argument tails
+ have the same structure as the direct argument tails described above.
+ Because @|va_list| objects are hard to copy, the keyword-argument support
+ library consistently passes @|va_list| objects \emph{by reference}
+ throughout its programming interface.
+\item Indirectly, through a vector of @|struct kwval| objects, each of which
+ contains a keyword name, as a pointer to a null-terminated string, and the
+ \emph{address} of a corresponding argument value. (This indirection is
+ necessary so that the items in the vector can be of uniform size.)
+ Argument vectors are rather inconvenient to use, but are the only practical
+ way in which a caller can decide at runtime which arguments to include in a
+ call, which is useful when writing wrapper functions.
+\end{enumerate}
+
+Keyword arguments are provided as a general feature for C functions.
+
%%%--------------------------------------------------------------------------
\section{Messages and methods} \label{sec:concepts.methods}
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}
###--------------------------------------------------------------------------
### The source files.
+pkginclude_HEADERS += keyword.h
+libsod_la_SOURCES += keyword.c keyword-hosted.c
+dist_man_MANS += keyword.3
+
pkginclude_HEADERS += sod.h
libsod_la_SOURCES += sod.c
dist_man_MANS += sod.3
--- /dev/null
+/* -*-c-*-
+ *
+ * Keyword-argument support functions requiring a hosted implementation
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensible Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with SOD; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @kw_defunknown@ --- *
+ *
+ * Arguments: @const char *set@ = keyword set name
+ * @const char *kw@ = the offending keyword name
+ *
+ * Returns: Doesn't.
+ *
+ * Use: This is the default @kw_unkhook@ hook function.
+ *
+ * In a hosted implementation, this function reports an internal
+ * error to stderr about the unknown keyword and calls @abort@.
+ * It is an implementation responsibility for freestanding
+ * implementations wanting to use this keyword argument
+ * mechanism.
+ */
+
+void kw_defunknown(const char *set, const char *kw)
+{
+ fprintf(stderr, "INTERNAL ERROR: unknown `%s' keyword argument `%s'\n",
+ set, kw);
+ abort();
+}
+
+/* --- @kw__hookfailed@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Called by @kw_unknown@ if the @kw_unkhook@ hook function
+ * returns.
+ *
+ * User code is not expected to call this function. It exists
+ * as an implementation respensibility for freestanding
+ * implementations wanting to use this keyword argument
+ * mechanism.
+ */
+
+void kw__hookfailed(void)
+{
+ fprintf(stderr, "INTERNAL ERROR: `kw_unkhook' hook function returned\n");
+ abort();
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+.\" -*-nroff-*-
+.\"
+.\" Keyword argument support
+.\"
+.\" (c) 2015 Straylight/Edgeware
+.\"
+.
+.\"----- Licensing notice ---------------------------------------------------
+.\"
+.\" This file is part of the Sensible Object Design, an object system for C.
+.\"
+.\" SOD is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU Library General Public License as
+.\" published by the Free Software Foundation; either version 2 of the
+.\" License, or (at your option) any later version.
+.\"
+.\" SOD is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU Library General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Library General Public
+.\" License along with SOD; if not, write to the Free
+.\" Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+.\" MA 02111-1307, USA.
+.
+.\" Highlight using terminal escapes, rather than overstriking.
+.\"\X'tty: sgr 1'
+.
+.\" String definitions and font selection.
+.ie t \{\
+. ds o \(bu
+. if \n(.g .fam P
+.\}
+.el \{\
+. ds o o
+.\}
+.
+.\" .hP TEXT -- start an indented paragraph with TEXT hanging off to the left
+.de hP
+.IP
+\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c
+..
+.
+.de t(
+'in +\\n(.ku
+..
+.de t)
+.in
+..
+.
+.\"--------------------------------------------------------------------------
+.TH keyword 3 "16 December 2015" "Straylight/Edgeware" "Sensible Object Design"
+.
+.SH NAME
+keyword \- keyword argument support library
+.
+.\"--------------------------------------------------------------------------
+.SH SYNOPSIS
+.B #include <sod/keyword.h>
+.PP
+.B "struct kwval { const char *kw; const void *val; };"
+.br
+.B "struct kwtab { const struct kwval *v; size_t n; };"
+.br
+.BI "typedef void kw_unkhookfn(const char *" set ", const char *" kw ");"
+.PP
+.BI "#define " set "_KWSET(_) \e"
+.in +4m
+.BI "_(" name ", " type ", " default ") \e"
+.br
+\&...
+.in
+.IB declaration-specifiers " KWSET_STRUCT(" set ");"
+.br
+.IB declaration-specifiers " KWSET_PARSEFN(" set ")"
+.PP
+.B KWCALL
+.IB type0 " " func "(" type1 " " arg1 ,
+.RB ... ,
+.IB typen " " argn ,
+.B "KWTAIL);"
+.br
+.BI "KWDECL(" set ", " kw ");"
+.br
+.BI "KW_PARSE(" set ", " kw ", " kwfirst ");"
+.br
+.BI "KW_PARSE_EMPTY(" set ", " kwfirst ");"
+.br
+.BI "KWPARSE(" set ");"
+.br
+.BI "KWPARSE_EMPTY(" set ");"
+.PP
+.I val
+.B =
+.IB func "(" arg1 ,
+.RB ... ,
+.IB argn ,
+.BI "KWARGS(" \c
+.t(
+.BI "K(" name ", " value ")"
+.br
+.BI "K_VALIST(" ap ")"
+.br
+.BI "K_TAB(" v ", " n ")"
+.br
+.RB ... );
+.t)
+.br
+.I val
+.B =
+.IB func "(" arg1 ,
+.RB ... ,
+.IB argn ,
+.B "NO_KWARGS);"
+.PP
+.B unsigned
+.BI "KW_COUNT(" set ");"
+.br
+.B void
+.BI "KW_COPY(" \c
+.t(
+.IB fromset ", " toset ","
+.br
+.BI "const struct " fromset "_kwset *" kw ","
+.br
+.BI "struct kwval *" v ", size_t " n ");"
+.t)
+.PP
+.BI "void kw_unknown(const char *" set ", const char *" kw );
+.br
+.BI "void kw_parseempty(\fP" \c
+.t(
+.BI "const char *" set ,
+.BI "const char *" kwfirst ,
+.BI "va_list *" ap ,
+.br
+.BI "const struct kwval *" v ,
+.BI "size_t " n );
+.t)
+.PP
+.B "kw_unkhookfn *kw_unkhook;"
+.br
+.B "kw_unkhookfn kw_defunknown;"
+.
+.\"--------------------------------------------------------------------------
+.SH DESCRIPTION
+.
+.SS Theory
+In standard C,
+the actual arguments provided to a function
+are matched up with the formal arguments
+given in the function definition
+according to their ordering in a list.
+Unless the (rather cumbersome) machinery for dealing with
+variable-length argument tails
+.RB ( <stdarg.h> )
+is used,
+exactly the correct number of arguments must be supplied,
+and in the correct order.
+.PP
+A
+.I keyword argument
+is matched by its distinctive
+.IR name ,
+rather than by its position in a list.
+Keyword arguments may be
+.IR omitted ,
+causing some default behaviour by the function.
+A function can detect whether
+a particular keyword argument was supplied:
+so the default behaviour need not be the same as
+that caused by any specific value of the argument.
+.PP
+Keyword arguments can be provided in three ways.
+.hP 1.
+Directly, as a variable-length argument tail,
+consisting (for the most part \(en see below) of alternating
+keyword names, as pointers to null-terminated strings, and
+argument values, and
+terminated by a null pointer.
+This is somewhat error-prone,
+and the support library defines some macros
+which help ensure that keyword argument lists are well formed.
+.hP 2.
+Indirectly, through a
+.B va_list
+object capturing a variable-length argument tail
+passed to some other function.
+Such indirect argument tails have the same structure as
+the direct argument tails described above.
+Because
+.B va_list
+objects are hard to copy,
+the keyword-argument support library consistently passes
+.B va_list
+objects
+.I by reference
+throughout its programming interface.
+.hP 3.
+Indirectly, through a vector of
+.B struct kwval
+objects,
+each of which contains
+a keyword name, as a pointer to a null-terminated string, and
+the
+.I address
+of a corresponding argument value.
+(This indirection is necessary so that
+the items in the vector can be of uniform size.)
+Argument vectors are rather inconvenient to use,
+but are the only practical way in which a caller can decide at runtime
+which arguments to include in a call,
+which is useful when writing wrapper functions.
+.
+.SS Type definitions
+The header file defines two simple structure types.
+.PP
+.IP
+.nf
+.ft B
+struct kwval {
+ const char *kw;
+ const void *val;
+};
+.fi
+.PP
+The
+.B kwval
+structure describes a keyword argument name/value pair.
+The
+.B kw
+member points to the name,
+as a null-terminated string.
+The
+.B val
+member always contains the
+.I address
+of the value.
+(This somewhat inconvenient arrangement
+makes the size of a
+.B kwval
+object independent of the actual argument type.)
+.PP
+.IP
+.nf
+.ft B
+struct kwtab {
+ const struct kwval *v;
+ size_t n;
+};
+.fi
+.PP
+The
+.B kwtab
+structure describes a list of keyword arguments,
+represented as a vector of
+.B kwval
+structures.
+The
+.B v
+member points to the start of the vector;
+the
+.B n
+member contains the number of elements in the vector.
+.PP
+The
+.B kw_unkhookfn
+type is the type of
+unknown-keyword handler functions.
+See the descriptions of
+.B kw_unknown
+and
+.B kw_unkhook
+below.
+.
+.SS Calling functions with keyword arguments
+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
+.IR "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.
+.PP
+That said, the underlying machinery is presented first,
+and the convenience macros are described later.
+.PP
+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
+.BR "const char *" ),
+and their argument values.
+This sequence is finally terminated by a null pointer
+(again with type
+.BR "const char *" )
+in place of a keyword name.
+.PP
+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.
+.TP
+.B kw.valist
+This special keyword is followed by a pointer to
+a variable-length argument tail cursor object, of type
+.BR "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
+.B KW_PARSEFN
+macro below for more details about these.)
+The argument tail may itself contain the special keywords.
+.TP
+.B kw.tab
+This special keyword is followed by
+.I two
+argument values:
+a pointer to the base of a vector of
+.B kwval
+structures,
+and the number of elements in this vector
+(as a
+.BR size_t ).
+Each element of the vector describes a single keyword argument:
+the
+.B kw
+member points to the keyword's name, and
+the
+.B val
+member points to the value.
+The vector may contain special keywords.
+The
+.B val
+pointer for a
+.B kw.valist
+argument should contain the address of an object of type
+.B "va_list *"
+(and not point directly to the cursor object,
+since
+.B val
+is has type
+.B "const void *"
+but the cursor will be modified as its argument tail is traversed).
+The
+.B val
+pointer for a
+.B kw.tab
+argument should contain the address of a
+.B kwtab
+structure which itself contains the base address and length of
+the argument vector to be processed.
+.TP
+.B kw.unknown
+This keyword is never accepted by any function.
+If it is encountered,
+the
+.B kw_unknown
+function is called to report the situation as an error;
+see below.
+.PP
+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.
+.PP
+The macro
+.BI "KWARGS(" body ")"
+wraps up a sequence of keyword arguments.
+The single
+.I body
+argument consists of a sequence of calls to
+the keyword-argument macros described below,
+one after another without any separation.
+.PP
+In C89, macro actual arguments are not permitted to be empty;
+if there are no keyword arguments to provide,
+then the argument-less macro
+.B NO_KWARGS
+should be used instead.
+If you're using C99 or later,
+it's fine to just write
+.B KWARGS()
+instead.
+.PP
+The following keyword-argument macros can be used
+within
+.BR KWARGS 's
+.I body
+argument.
+.TP
+.BI "K(" name ", " value ")"
+Passes a keyword name and its corresponding value,
+as a pair of arguments.
+The
+.I name
+should be a single identifier
+(not a quoted string).
+The
+.I value
+may be any C expression
+of the appropriate type.
+.TP
+.BI "K_VALIST(" ap ")"
+Passes an indirect variable-length argument tail.
+The argument
+.I ap
+should be an lvalue of type
+.B va_list
+which will be passed by reference.
+.TP
+.BI "K_TAB(" v ", " n ")"
+Passes a vector of keyword arguments.
+The argument
+.I v
+should be the base address of the vector, and
+.I n
+should be the number of elements in the vector.
+.
+.SS Defining functions with keyword arguments
+A
+.I "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
+.B KWPARSE_EMPTY
+macro described below.)
+.PP
+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
+.B kw_unknown
+protocol (described below),
+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.
+.PP
+The keyword arguments for a keyword set named
+.I set
+are described by a `list macro' named
+.IB set _KWSET \fR.
+This macro takes a single argument,
+conventionally named
+.RB ` _ '.
+It should expand to a sequence of one or more list items of the form
+.IP
+.BI "_(" type ", " name ", " default ")"
+.PP
+with no separation between them.
+.PP
+For example:
+.IP
+.nf
+.ft B
+#define example_KWSET(_) \e
+.in +4m
+_(int, x, 0) \e
+_(const char *, y, NULL)
+.fi
+.ft P
+.PP
+Each
+.I name
+should be a distinct C identifier;
+they will be used to name structure members.
+An argument
+.I name
+should not end with the suffix
+.RB ` _suppliedp '
+(for reasons which will soon become apparent).
+.PP
+Each
+.I type
+should be a C
+.I type-name
+such that
+.IP
+.IB type " " name ;
+.PP
+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 must be placed after the
+.IR name ).
+This is the same requirement made by the standard
+.BR va_arg (3)
+macro.
+.PP
+Each
+.I 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.)
+.PP
+The macro
+.B 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
+.B KWTAIL
+marker should be separated from them with a comma
+.RB ` , '.
+(It is permitted for a function parameter type list to contain
+only a
+.B KWTAIL
+marker.)
+.PP
+Specifically,
+the macro declares a mandatory argument
+.B const char *kwfirst_
+(to collect the first keyword name),
+and a variable-length argument tail.
+.PP
+The macro
+.B KWPARSE
+(described below)
+assumes that the enclosing function's argument list ends with a
+.B KWTAIL
+marker.
+The marker should be included both in the function's definition and
+in any declarations, e.g., in the corresponding header file.
+.PP
+The
+.B 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.
+.PP
+The
+.B KWSET_STRUCT
+macro defines a
+.IR "keyword structure" .
+If
+.I set
+is a keyword-set name then
+.IP
+.BI "KWSET_STRUCT(" set ");"
+.PP
+declares a structure
+.B struct
+.IB set _kwargs \fR.
+For each argument defined in the keyword set,
+this structure contains two members:
+one has exactly the
+.I name
+and
+.I type
+listed in the keyword set definition;
+the other is a 1-bit-wide bitfield of type
+.B "unsigned int"
+named
+.IB name _suppliedp \fR.
+.PP
+The macro
+.B KWDECL
+declares and initializes a keyword argument structure variable.
+If
+.I set
+is a keyword-set name then
+.IP
+.I declaration-specifiers
+.BI "KWDECL(" set ", " kw ");"
+.PP
+declares a variable of type
+.B struct
+.IB set _kwargs
+named
+.IR kw .
+The optional
+.I declaration-specifiers
+may provide additional storage-class,
+qualifiers,
+or other declaration specifiers.
+The
+.RB ` _suppliedp '
+flags are initialized to zero;
+the other members are initialized with the corresponding defaults
+from the keyword-set definition.
+.PP
+The macro
+.B KWSET_PARSEFN
+defines a keyword argument
+.IR "parser function" .
+If
+.I set
+is a keyword-set name then
+.IP
+.I declaration-specifiers
+.BI "KWSET_PARSEFN(" set ")"
+.PP
+(no trailing semicolon!)
+defines a function
+.IP
+.B void
+.IB set _kwparse( \c
+.t(
+.BI "struct " set "_kwargs *" kw ","
+.br
+.BI "const char *" kwfirst ", va_list *" ap ","
+.br
+.BI "const struct kwval *" v ", size_t " n ");"
+.t)
+.PP
+The macro call can
+(and usually will)
+be preceded by storage class specifiers such as
+.BR static ,
+for example to adjust the linkage of the name.
+(I don't recommend declaring parser functions
+.BR inline :
+parser functions are somewhat large, and
+modern compilers are pretty good at
+figuring out whether to inline static functions.)
+.PP
+The function's behaviour is as follows.
+It parses keyword arguments from
+a variable-length argument tail, and/or
+a vector of
+.B kwval
+structures.
+When a keyword argument is recognized,
+for some keyword
+.IR name ,
+the keyword argument structure pointed to by
+.I kw
+is updated:
+the flag
+.IB name _suppliedp
+is set to 1;
+and the argument value is stored (by simple assignment) in the
+.I name
+member.
+Hence, if the
+.RB ` _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
+.I v
+is scanned
+.I after
+the variable-length argument tail captured in
+.IR ap .)
+.PP
+The variable-argument tail is read from the list described by
+.BI * ap \fR.
+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
+.B "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
+.I kwfirst
+argument;
+the first argument retrieved using the
+.B va_list
+cursor object should then be the value
+corresponding to the keyword named by
+.IR kwfirst .
+(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
+.I kwfirst
+is a null pointer,
+then
+.I ap
+need not be a valid pointer;
+otherwise, the cursor object
+.BI * ap
+will be modified as the function extracts
+successive arguments from the tail.
+.PP
+The keyword vector is read from the vector of
+.B kwval
+structures starting at address
+.I v
+and containing the following
+.I n
+items.
+If
+.I n
+is zero then
+.I v
+need not be a valid pointer.
+.PP
+The function also handles the special
+.B kw.valist
+and
+.B kw.tab
+arguments described above.
+If an unrecognized keyword argument is encountered,
+then
+.B kw_unknown
+is called:
+see below for details.
+.PP
+The
+.B KW_PARSE
+macro invokes a keyword argument parsing function.
+If
+.I set
+is a keyword-set name,
+.I kw
+names a keyword argument structure variable of type
+.B struct
+.IB set _kwargs \fR,
+and
+.I kwfirst
+is the name of the enclosing function's last mandatory argument,
+which must have type
+.BR "const char *" ,
+then
+.IP
+.BI "KW_PARSE(" set ", " kw ", " kwfirst ");"
+.PP
+calls the function
+.IB set _kwparse
+with five arguments:
+the address of the keyword argument structure
+.IR kw ;
+the string pointer
+.IR kwfirst ;
+the address of a temporary argument-tail cursor object of type
+.BR va_list ,
+constructed on the assumption that
+.I 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
+.I kw
+was declared using
+.B KWDECL
+and the function
+.IB set _kwparse
+has been defined using
+.B KWSET_PARSEFN
+then the effect is to parse the keyword arguments passed to the function
+and set the members of
+.I kw
+appropriately.
+.PP
+The macro
+.B KWPARSE
+(note the lack of underscore)
+combines
+.B KWDECL
+and
+.BR KW_PARSE .
+If
+.I set
+is a keyword-set name then
+.IP
+.BI "KWPARSE(" set ");"
+.PP
+declares and initializes a keyword argument structure variable
+with the fixed name
+.BR kw ,
+and parses the keyword arguments provided to the enclosing function,
+storing the results in
+.BR kw .
+It assumes that the first keyword name
+is in an argument named
+.BR kwfirst_ ,
+as set up by
+.B KWTAIL marker described above.
+.PP
+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
+.B kw_unknown
+handler if they encounter an unknown keyword,
+and the calling function will not get a chance
+to tidy up after itself if this happens.
+.PP
+As mentioned above,
+it is not permitted to define an empty keyword set.
+(Specifically, invoking
+.B 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.
+The macros
+.B KW_PARSE_EMPTY
+and
+.B KWPARSE_EMPTY
+are analogues of
+.B KW_PARSE
+and
+.B KWPARSE
+respectively,
+and handle this case.
+These macros take a keyword-set name as an argument,
+but this name is used only in diagnostic messages
+(e.g., if an unknown keyword name is encountered)
+and need not
+(and probably should not)
+correspond to a defined keyword set.
+.PP
+If
+.I set
+is an identifier then
+.IP
+.BI "KW_PARSE_EMPTY(" set ", " kwfirst ");"
+.PP
+calls the function
+.B kw_parseempty
+with five arguments:
+the
+.I set
+name, as a string;
+the string pointer
+.IR kwfirst ;
+the address of a temporary argument-tail cursor object of type
+.BR va_list ,
+constructed on the assumption that
+.I 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.
+.PP
+If
+.I set
+is an identifier then
+.IP
+.B "KWPARSE_EMPTY(" set ");"
+.PP
+(note the lack of underscore)
+checks that the enclosing function has been passed
+no keyword arguments other than the special predefined ones.
+It assumes that the function's parameter type list ends with the
+.B KWTAIL
+marker described above.
+.PP
+The
+.B kw_parseempty
+function checks an keyword argument list
+to make sure that contains no keyword arguments
+(other than the special ones described above).
+.PP
+The
+.I set
+argument should point to a null-terminated string:
+this will be reported as the keyword set name to
+.BR 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
+.B KWSET_PARSEFN
+macro.
+.
+.SS "Wrapper functions"
+Most users will not need the hairy machinery involving argument vectors.
+Their main use is in defining
+.IR "wrapper functions" .
+Suppose there is a function
+.I f
+which accepts some keyword arguments,
+and we want to write a function
+.I g
+which accepts the same keywords recognized by
+.I f
+and some additional ones.
+Unfortunately
+.I 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
+.B 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
+.IR g ,
+by building a vector of keyword arguments,
+collected one-by-one depending on the corresponding
+.RB ` _suppliedp '
+flags (see below).
+A few macros are provided to make this task easier.
+.PP
+The macro
+.B KW_COUNT
+returns the number of keywords defined in a keyword set.
+If
+.I set
+is a keyword-set name, then
+.IP
+.BI "KW_COUNT(" set ")"
+.PP
+returns the number of keywords defined by
+.IR set ,
+as a constant expression of type
+.BR "unsigned int" .
+.PP
+The macro
+.B KW_COPY
+populates a vector of
+.B kwval
+structures from a keyword-argument structure.
+If
+.I fromset
+and
+.I toset
+are two keyword-set names then
+.IP
+.BI "KW_COPY(" fromset ", " toset ", " kw ", " v ", " n ");"
+.PP
+will populate the vector
+.IR v ,
+taking argument values from
+.IR kw .
+The
+.I toset
+must be a subset of
+.IR fromset :
+i.e., for every keyword defined in
+.I toset
+there is a keyword defined in
+.I fromset
+with the same name and type.
+The remaining arguments are as follows:
+.I kw
+is a pointer to a
+.BI "struct " fromset "_kwset"
+keyword-argument structure which has been filled in,
+e.g., by the keyword-argument parsing function
+.IB fromset _kwparse \fR;
+.I v
+is a pointer to a sufficiently large vector of
+.B "struct kwval"
+objects;
+and
+.I n
+is an lvalue designating an object of integer type.
+Successive elements of
+.IR v ,
+starting at index
+.IR n ,
+are filled in to refer to
+the keyword arguments defined in
+.I toset
+whose
+.RB ` _suppliedp '
+flag is set in the argument structure pointed to by
+.IR kw ;
+for each such argument,
+a pointer to the keyword name is stored in
+the corresponding vector element's
+.B kw
+member, and
+a pointer to the argument value,
+held in the keyword argument structure,
+is stored in the vector element's
+.B val
+member.
+At the end of this,
+the index
+.I n
+is advanced so as to contain the index of the first unused element of
+.IR v .
+Hence, at most
+.BI KW_COUNT( toset )
+elements of
+.I v
+will be used.
+.
+.SS Handling unknown-keyword errors
+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.
+.PP
+In this situation,
+the parser functions generated by
+.B KW_PARSEFN
+(and the
+.B kw_parseempty
+function)
+call
+.BR kw_unknown .
+This is a function of two arguments:
+.I set
+points to the name of the keyword set expected by the caller,
+as a null-terminated string; and
+.I kw
+is the unknown keyword which was encountered.
+All that
+.B kw_unknown
+does is invoke the function whose address is stored in
+the global variable
+.B kw_unkhook
+with the same arguments.
+The
+.B kw_unknown
+function never returns to its caller:
+if the
+.B kw_unkhook
+function returns (which it shouldn't)
+then
+.B kw_unknown
+writes a fatal error message to standard error
+and calls
+.BR abort (3).
+.PP
+By default
+.B kw_unkhook
+points to the function
+.BR kw_defunknown ,
+which just writes an error message
+quoting the keyword set name
+and offending keyword
+to standard error
+and calls
+.BR abort (3).
+.PP
+(In freestanding environments,
+the behaviour may be somewhat different:
+porting the library to such environments involves
+choosing appropriate behaviour for the target platform.)
+.PP
+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.
+.IP
+.nf
+.ft B
+#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)
+{
+ if (strcmp(kw, "kw.unknown")) longjmp(kw_test_jmp, 1);
+ else longjmp(kw_test_jmp, 2);
+}
+
+#define KW_TEST(flag, set, call) do { \e
+ kw_unkhookfn *oldunk = kw_unkhook; \e
+ kw_unkhook = kw_test_unknown; \e
+ switch (setjmp(kw_test_jmp)) { \e
+ case 0: call; abort(); \e
+ case 1: flag = 1; break; \e
+ case 2: flag = 0; break; \e
+ default: abort(); \e
+ } \e
+ kw_unkhook = oldunk; \e
+} 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)
+ */
+.ft P
+.fi
+.
+.\"--------------------------------------------------------------------------
+.SH BUGS
+.
+The unknown-keyword hook is inadequate for a modern library,
+but dealing with multiple threads isn't currently possible
+without writing (moderately complex) system-specific code.
+The author's intention is that the hook variable
+.B 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
+.BR mLib ;
+other platforms will likely need different arrangements.
+The author is willing to coordinate any such efforts.
+.PP
+The whole interface is rather clunky.
+Working with keyword-argument vectors is especially unpleasant.
+The remarkable thing is not that it's done well,
+but that it can be done at all.
+.
+.\"--------------------------------------------------------------------------
+.SH SEE ALSO
+.
+.BR va_start (3),
+.BR va_arg (3),
+.BR va_end (3).
+.
+.\"--------------------------------------------------------------------------
+.SH AUTHOR
+.
+Mark Wooding,
+<mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
--- /dev/null
+/* -*-c-*-
+ *
+ * Keyword argument handling
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensible Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with SOD; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "keyword.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+kw_unkhookfn *kw_unkhook = kw_defunknown;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @kw_unknown@ --- *
+ *
+ * Arguments: @const char *set@ = the keyword set name, as a string
+ * @const char *kw@ = the unknown keyword argument, as a string
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Called when an unrecognized keyword argument is encountered
+ * during parsing. This calls the @kw_unkhook@ with the same
+ * arguments. Recovery via @longjmp@ or a similar machanism is
+ * acceptable.
+ */
+
+void kw_unknown(const char *set, const char *kw)
+ { kw_unkhook(set, kw); kw__hookfailed(); }
+
+/* --- @kw_parseempty@ --- *
+ *
+ * Arguments: @const char *set@ = the keyword set name, as a string
+ * @const char *kwfirst@ = the first keyword argument name
+ * @va_list *ap@ = pointer to argument-tail extraction state
+ * @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = size of argument vector
+ *
+ * Returns: ---
+ *
+ * Use: Goes through the motions of parsing keyword arguments, but
+ * doesn't in fact handle any other than the standard ones
+ * described above (see @KWSET_PARSEFN@). This is useful when a
+ * function doesn't currently define any keyword arguments but
+ * wants to reserve the right to define some in the future.
+ * (The usual machinery can't be used in this case, since the
+ * argument structure would be empty. Besides, it would be
+ * pointless to include multiple copies of the same boilerplate
+ * code in a program.)
+ */
+
+void kw_parseempty(const char *set, const char *kwfirst, va_list *ap,
+ const struct kwval *v, size_t n)
+{
+ const char *k, *kk;
+ va_list *aap;
+ const struct kwtab *t;
+ const struct kwval *vv;
+ size_t nn;
+
+ for (k = kwfirst; k; k = va_arg(*ap, const char *)) {
+ if (!strcmp(k, "kw.va_list")) {
+ aap = va_arg(*ap, va_list *);
+ kk = va_arg(*aap, const char *);
+ kw_parseempty(set, kk, aap, 0, 0);
+ } else if (!strcmp(k, "kw.tab")) {
+ vv = va_arg(*ap, const struct kwval *);
+ nn = va_arg(*ap, size_t);
+ kw_parseempty(set, 0, 0, vv, nn);
+ } else
+ kw_unknown(set, k);
+ }
+ while (n) {
+ if (!strcmp(v->kw, "kw.va_list")) {
+ aap = *(va_list *const *)v->val;
+ kk = va_arg(*aap, const char *);
+ kw_parseempty(set, kk, aap, 0, 0);
+ } else if (!strcmp(k, "kw.tab")) {
+ t = (const struct kwtab *)v->val;
+ kw_parseempty(set, 0, 0, t->v, t->n);
+ } else
+ kw_unknown(set, k);
+ v++; n--;
+ }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Keyword argument handling
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensible Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with SOD; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef KEYWORD_H
+#define KEYWORD_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+/*----- Function annotations ----------------------------------------------*/
+
+/* Some macros are defined for annotating functions. They may improve
+ * compiler diagnostics when used properly. They should be included as part
+ * of the function's declaration specifiers.
+ *
+ * * @KWCALL@ marks a function as expecting keyword arguments. The
+ * compiler may check that there are an odd number of arguments, that the
+ * even-numbered (starting from zero) arguments have pointer-to-character
+ * type, and that the final argument is null.
+ *
+ * * @KW__NORETURN@ marks a function as never returning. Applied to
+ * @kw_unknown@ and its various friends. Users are not expected to use
+ * this.
+ */
+
+#if defined(__GNUC__)
+# define KW__GCC_VERSION_P(maj, min) \
+ (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))
+# if KW__GCC_VERSION_P(2, 5)
+# define KW__NORETURN __attribute__((__noreturn__))
+# endif
+# if KW__GCC_VERSION_P(4, 0)
+# define KWCALL __attribute__((__sentinel__))
+# endif
+#endif
+
+/* --- Trivial definitions, if we don't have compiler support --- */
+
+#ifndef KW__NORETURN
+# define KW__NORETURN
+#endif
+
+#ifndef KWCALL
+# define KWCALL
+#endif
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* A keyword/value pair. A vector of these can be passed as the value of the
+ * special keyword `kw.tab'. This is a rather cumbersome way of constructing
+ * lists of keyword arguments for a function in a programmatic way.
+ */
+struct kwval {
+ const char *kw; /* The keyword name, as a string */
+ const void *val; /* A pointer to the keyword value */
+};
+
+/* A table of keyword/value pairs. This is used as the value of a `kw.tab'
+ * argument which is itself in a @struct kwval@ table, since it's not
+ * possible to store both the vector and length directly.
+ */
+struct kwtab {
+ const struct kwval *v; /* The address of the vector */
+ size_t n; /* The number of keywords */
+};
+
+/* The type of unknown-keyword handler functions. */
+typedef void kw_unkhookfn(const char */*set*/, const char */*kw*/);
+
+/*----- Global variables --------------------------------------------------*/
+
+/* A global hook function for handling unknown-keyword errors. The default
+ * function prints a message to the standard error stream and aborts.
+ *
+ * The hook function must not return. It's not possible to recover from an
+ * unknown-keyword error while parsing a variable-length argument tail, since
+ * it's impossible to find out what type the corresponding argument value is.
+ *
+ * Having a single global hook isn't really very satisfactory, but a fully
+ * adequate solution gets complicated quickly. An external library will
+ * eventually be available to pick up the slack.
+ */
+extern kw_unkhookfn *kw_unkhook;
+
+/*----- Argument list macros ----------------------------------------------*/
+
+/* These macros is intended to be conveniences rather than a proper
+ * abstraction. Functions with more complicated interfaces, and their
+ * callers, will have to make their own arrangements.
+ */
+
+/* --- @KWTAIL@ --- *
+ *
+ * Arguments: ---
+ *
+ * Use: Marker to be included in a function prototype (at the end of
+ * the argument list) to indicate that the function accepts
+ * keyword arguments. It is acceptable for the @KWTAIL@ marker
+ * to be only thing in the argument list.
+ */
+
+#define KWTAIL const char *kwfirst_, ...
+
+/* --- @KWARGS@ --- *
+ *
+ * Arguments: @body@ = a sequence of @K(kw, value)@ macro calls, without
+ * separators
+ *
+ * Use: A package of actual keyword arguments. In C89, the @body@
+ * must not be empty: to pass no keywords, use @NO_KWARGS@
+ * instead.
+ */
+
+#define KWARGS(body) body KW__END
+#define KW__END ((const char *)0)
+
+/* --- @NO_KWARGS@ --- *
+ *
+ * Arguments: ---
+ *
+ * Use: Special marker to include in an actual argument list to
+ * indicate that no keyword arguments are to be passed. See
+ * @KWARGS@ above.
+ */
+
+#define NO_KWARGS KW__END, KW__END
+ /* Slight hack. The @KWCALL@ macro sets GCC and similar compilers up to
+ * check for a sentinal null pointer at the end of the variable-length
+ * argument tail. Alas, if there are no keywords at all, then the null
+ * terminator ends up in the @kwfirst_@ argument, and the tail is propetly
+ * empty, with the result that the compiler gives an annoying warning.
+ * Supplying an extra argument here is obviously harmless, and makes the
+ * otherwise useful warning go away in this case where it's not wanted.
+ */
+
+/* --- @K@ --- *
+ *
+ * Arguments: @kw@ = keyword name, as an unquoted token list
+ * @val@ = keyword value, as an expression
+ *
+ * Use: Bundles a keyword @kw@ and value @val@ together.
+ */
+
+#define K(kw, val) #kw, (val),
+
+/* --- @KW_VALIST@ --- *
+ *
+ * Arguments: @va_list ap@ = argument-list extraction state
+ *
+ * Use: Passes a reified variable-length argument tail into a keyword
+ * function.
+ */
+
+#define K_VALIST(ap) "kw.valist", &(ap),
+
+/* --- @KW_TAB@ --- *
+ *
+ * Arguments: @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = length of argument vector
+ *
+ * Use: Passes an vector of keyword arguments into a keyword
+ * function.
+ */
+
+#define K_TAB(v, n) "kw.tab", (v), (size_t)(n),
+
+/*----- Keyword set definitions -------------------------------------------*
+ *
+ * A `keyword set' describes the collection of keyword arguments to be
+ * accepted by a function (or group of functions). Keyword sets have names,
+ * which are C identifiers. A keyword set must not be empty: use
+ * @kw_parseempty@ instead of this machinery when defining a function which
+ * may later accept keyword arguments but which currently doesn't define any.
+ *
+ * A keyword set definition is a macro of a single argument, conventionally
+ * named `@_@'. The macro for a keyword set called @foo@ is named
+ * @foo_KWSET@. It should consist of a triple @_(type, key, dflt)@ for each
+ * keyword argument, where @type@ is the C type of the argument, @key@ is the
+ * name of the argument (as a C identifier), and @dflt@ is an expression
+ * (valid to use in an aggregate initializer) to provide the default value
+ * for the argument. The @type@ must be such that @type *@ is the type of a
+ * pointer to object of @type@.
+ */
+
+/* --- @KWSET_STRUCT@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: Defines the keyword set argument structure @struct
+ * set_kwargs@.
+ *
+ * The structure is used to communicate argument values between
+ * a function accepting keyword arguments and the argument
+ * parsing function constructed by @KWSET_PARSEFN@. It contains
+ * two members for each keyword argument: one with the name of
+ * the argument and the appropriate type to hold its value; the
+ * other is a one-bit-wide bitfield named with a `_suppliedp'
+ * suffix, and is set to indicate whether the caller provided a
+ * a value for the corresponding keyword argument.
+ */
+
+#define KWSET_STRUCT(set) \
+ struct set##_kwargs { \
+ set##_KWSET(KWSET__SUPPLIEDP) \
+ set##_KWSET(KWSET__STRUCTMEM) \
+ }
+#define KWSET__SUPPLIEDP(type, name, dflt) unsigned name##_suppliedp : 1;
+#define KWSET__STRUCTMEM(type, name, dflt) type name;
+
+/* --- @KWSET_PARSEFN@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: Defines the keyword argument parsing function @set_kwparse@.
+ * A call to this macro may be preceded by a storage-class
+ * specifier, e.g., @static@, to specify the linkage for the
+ * parsing function's name.
+ *
+ * This function takes five arguments:
+ *
+ * @struct set_kwargs *kw@ = pointer to keyword set argument
+ * structure to fill in
+ * @const char *kwfirst@ = first keyword argument name from the
+ * variable-length argument tail, or null if the
+ * argument tail is empty
+ * @va_list *ap@ = pointer to variable-length tail extraction
+ * state object
+ * @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = length of argument vector
+ *
+ * The `kwparse' function extracts keyword arguments from the
+ * argument tail (via @*ap@), and then the vector @v@; it
+ * updates the structure @*kw@ with their values, and sets the
+ * `_suppliedp' flags accordingly. It's unusual to call the
+ * `kwparse' function with both a nontrivial argument tail and
+ * vector, but the effect is nonetheless well-defined.
+ *
+ * The argument tail consists of alternating keyword argument
+ * names (as pointers to null-terminated strings) and values,
+ * terminated by a null pointer. The argument values are simply
+ * copied into the structure. Passing the @kwfirst@ argument
+ * separately allows functions to declare an explicit positional
+ * argument for the first keyword name, which is useful if the
+ * function has no other positional arguments.
+ *
+ * The argument vector consists of @struct kwval@ items, each of
+ * which contains a keyword name (as a pointer to a null-
+ * terminated string) and the address of its value. Argument
+ * values are again copied into the structure. Note that a
+ * vector doesn't store the arguments directly. This makes them
+ * rather cumbersome to set up, but the benefit is a simple and
+ * uniform approach for all keyword arguments.
+ *
+ * The main application for argument vectors is for `front-end'
+ * functions which want to pass on some subset of their keywords
+ * to another function. There isn't currently any macrology
+ * provided for achieving this, but it's not especially
+ * difficult.
+ *
+ * There are (currently) two special keyword arguments, whose
+ * names are not valid identifiers. Future additions will also
+ * have names beginning `kw.'.
+ *
+ * * `kw.valist' -- the corresponding argument has type
+ * @va_list *@, and represents an entire variable-length
+ * argument tail to process, including the first keyword
+ * name.
+ *
+ * * `kw.tab' -- the corresponding argument is a vector of
+ * @struct kwval@ items to process. In a variable-length
+ * argument tail, this is passed as two arguments: the base
+ * address of the vector, and the length (as a @size_t@).
+ * In an argument vector, this is passed instead as a value
+ * of type @struct kwtab@.
+ *
+ * If an unknown keyword is encountered while parsing, the
+ * function @kw_unknown@ is called.
+ *
+ * The keyword argument `kw.unknown' will never be defined.
+ */
+
+#define KWSET_PARSEFN(set) \
+ void set##_kwparse(struct set##_kwargs *kw, \
+ const char *kwfirst, va_list *ap, \
+ const struct kwval *v, size_t n) \
+ { \
+ const char *k, *kk; \
+ va_list *aap; \
+ const struct kwtab *t; \
+ const struct kwval *vv; \
+ size_t nn; \
+ \
+ for (k = kwfirst; k; k = va_arg(*ap, const char *)) { \
+ if (!strcmp(k, "kw.valist")) { \
+ aap = va_arg(*ap, va_list *); \
+ kk = va_arg(*aap, const char *); \
+ set##_kwparse(kw, kk, aap, 0, 0); \
+ } else if (!strcmp(k, "kw.tab")) { \
+ vv = va_arg(*ap, const struct kwval *); \
+ nn = va_arg(*ap, size_t); \
+ set##_kwparse(kw, 0, 0, vv, nn); \
+ } \
+ set##_KWSET(KWSET__ARGVA) \
+ else kw_unknown(#set, k); \
+ } \
+ \
+ while (n) { \
+ if (!strcmp(v->kw, "kw.valist")) { \
+ aap = *(va_list *const *)v->val; \
+ kk = va_arg(*aap, const char *); \
+ set##_kwparse(kw, kk, aap, 0, 0); \
+ } else if (!strcmp(v->kw, "kw.tab")) { \
+ t = (const struct kwtab *)v->val; \
+ set##_kwparse(kw, 0, 0, t->v, t->n); \
+ } \
+ set##_KWSET(KWSET__ARGTAB) \
+ else kw_unknown(#set, v->kw); \
+ v++; n--; \
+ } \
+ }
+#define KWSET__ARGVA(type, name, dflt) \
+ else if (!strcmp(k, #name)) { \
+ kw->name##_suppliedp = 1; \
+ kw->name = va_arg(*ap, type); \
+ }
+#define KWSET__ARGTAB(type, name, dflt) \
+ else if (!strcmp(v->kw, #name)) { \
+ kw->name##_suppliedp = 1; \
+ kw->name = *(type const *)v->val; \
+ }
+
+/*----- Defining keyword-accepting functions ------------------------------*/
+
+/* --- @KWDECL@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ * @kw@ = the name for the keyword argument structure value
+ *
+ * Use: Declares and initializes a keyword argument structure object
+ * @kw@. The `_suppliedp' members are initially all zero; the
+ * argument value members are set to their default values as
+ * specified in the keyword set definition macro.
+ */
+
+#define KWDECL(set, kw) \
+ struct set##_kwargs kw = \
+ { set##_KWSET(KWSET__SPINIT) set##_KWSET(KWSET__DFLT) }
+#define KWSET__SPINIT(type, name, dflt) 0,
+#define KWSET__DFLT(type, name, dflt) dflt,
+
+/* --- @KW_PARSE@, @KW_PARSE_EMPTY@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ * @kw@ = the name of the keyword argument structure
+ * @kwfirst@ = the first keyword argument name from the
+ * variable-length argument tail (and, therefore, the
+ * final positional argument)
+ *
+ * Use: Invokes the appropriate `kwparse' function to process the
+ * function's variable-length argument tail as keyword
+ * arguments.
+ *
+ * It is recommended that functions avoid allocating resources
+ * or making observable changes to program state until they have
+ * successfully parsed their keyword arguments.
+ *
+ * It is not possible to define an empty keyword argument set.
+ * If a function currently accepts no keyword argumets, but
+ * wants to reserve the ability to accept them later, then it
+ * should use @KW_PARSE_EMPTY@ (or, better, @KWPARSE_EMPTY@
+ * below). The keyword argument set name here is used only for
+ * diagnostic purposes, and need not (and probably should not)
+ * correspond to a keyword-set definition macro.
+ */
+
+#define KW_PARSE(set, kw, kwfirst) do { \
+ va_list ap_; \
+ va_start(ap_, kwfirst); \
+ set##_kwparse(&(kw), kwfirst, &ap_, 0, 0); \
+ va_end(ap_); \
+} while (0)
+
+#define KW_PARSE_EMPTY(set, kwfirst) do { \
+ va_list ap_; \
+ va_start(ap_, kwfirst); \
+ kw_parseempty(#set, kwfirst, &ap, 0, 0); \
+ va_end(ap_); \
+} while (0)
+
+/* --- @KWPARSE@, @KWPARSE_EMPTY@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: All-in-one keyword parsing for simple cases.
+ *
+ * This declares a keyword argument structure literally named
+ * @kw@, and parses the function's variable-length argument tail
+ * on the assumption that the function's argument list prototype
+ * contains a @KWTAIL@ marker.
+ *
+ * It is recommended that functions avoid allocating resources
+ * or making observable changes to program state until they have
+ * successfully parsed their keyword arguments.
+ *
+ * In C89, this macro must be placed precisely between the
+ * declarations at the start of the function body, and the
+ * statements after them.
+ *
+ * It is not possible to define an empty keyword argument set.
+ * If a function currently accepts no keyword argumets, but
+ * wants to reserve the ability to accept them later, then it
+ * should use @KWPARSE_EMPTY@. The keyword argument set name
+ * here is used only for diagnostic purposes, and need not (and
+ * probably should not) correspond to a keyword-set definition
+ * macro.
+ */
+
+#define KWPARSE(set) KWDECL(set, kw); KW_PARSE(set, kw, kwfirst_)
+
+#define KWPARSE_EMPTY(set) KW_PARSE_EMPTY(set, kwfirst_)
+
+/* --- @KW_COUNT@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: Expands to the number of keywords defined in the @set@.
+ */
+
+#define KW_COUNT(set) (0u set##_KWSET(KW__COUNT))
+#define KW__COUNT(type, name, dflt) + 1u
+
+/* --- @KW_COPY@ --- *
+ *
+ * Arguments: @fromset@ = the source keyword set name
+ * @toset@ = the destination keyword set name
+ * @kw@ = the source keyword argument structure
+ * @v@ = the destination vector
+ * @n@ = next free index in vector
+ *
+ * Use: Copies arguments from the source structure @kw@ into the
+ * vector @v@. The structure @kw@ must have type @struct
+ * fromset_kwargs *@. The argument @v@ must have type @struct
+ * kwval *@ (after array-to- pointer decay), and there must be a
+ * variable @v_n@ of sufficiently large integral type suitably
+ * initialized. Elements of the vector, starting with element
+ * @n@, will be filled in with those keyword arguments defined
+ * in @toset@ -- which must be a subset of @srcsrc@ from @kw@
+ * for which the `_suppliedp' flags are set. The @val@ members
+ * will point directly into the @kw@ structure. The @n@
+ * counter will be updated, and on completion will contain the
+ * index of the first unused entry in the vector.
+ */
+
+#define KW_COPY(fromset, toset, kw, v, n) do { \
+ const struct fromset##_kwargs *kw_ = &(kw); \
+ struct kwval *v_ = (v); \
+ size_t n_ = (n); \
+ toset##_KWSET(KW__COPY) \
+ (n) = n_; \
+} while (0)
+
+#define KW__COPY(type, name, dflt) \
+ if (kw_->name##_suppliedp) { \
+ v_[n_].kw = #name; \
+ v_[n_].val = &kw_->name; \
+ n_++; \
+ }
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @kw_unknown@ --- *
+ *
+ * Arguments: @const char *set@ = the keyword set name, as a string
+ * @const char *kw@ = the unknown keyword argument, as a string
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Called when an unrecognized keyword argument is encountered
+ * during parsing. This calls the @kw_unkhook@ with the same
+ * arguments. Recovery via @longjmp@ or a similar machanism is
+ * acceptable.
+ */
+
+extern KW__NORETURN void kw_unknown(const char */*set*/, const char */*kw*/);
+
+/* --- @kw_defunknown@ --- *
+ *
+ * Arguments: @const char *set@ = keyword set name
+ * @const char *kw@ = the offending keyword name
+ *
+ * Returns: Doesn't.
+ *
+ * Use: This is the default @kw_unkhook@ hook function.
+ *
+ * In a hosted implementation, this function reports an internal
+ * error to stderr about the unknown keyword and calls @abort@.
+ * It is an implementation responsibility for freestanding
+ * implementations wanting to use this keyword argument
+ * mechanism.
+ */
+
+extern KW__NORETURN kw_unkhookfn kw_defunknown;
+
+/* --- @kw__hookfailed@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Called by @kw_unknown@ if the @kw_unkhook@ hook function
+ * returns.
+ *
+ * User code is not expected to call this function. It exists
+ * as an implementation respensibility for freestanding
+ * implementations wanting to use this keyword argument
+ * mechanism.
+ */
+
+extern KW__NORETURN void kw__hookfailed(void);
+
+/* --- @kw_parseempty@ --- *
+ *
+ * Arguments: @const char *set@ = the keyword set name, as a string
+ * @const char *kwfirst@ = the first keyword argument name
+ * @va_list *ap@ = pointer to argument-tail extraction state
+ * @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = size of argument vector
+ *
+ * Returns: ---
+ *
+ * Use: Goes through the motions of parsing keyword arguments, but
+ * doesn't in fact handle any other than the standard ones
+ * described above (see @KWSET_PARSEFN@). This is useful when a
+ * function doesn't currently define any keyword arguments but
+ * wants to reserve the right to define some in the future.
+ * (The usual machinery can't be used in this case, since the
+ * argument structure would be empty. Besides, it would be
+ * pointless to include multiple copies of the same boilerplate
+ * code in a program.)
+ */
+
+extern void kw_parseempty(const char */*set*/,
+ const char */*kwfirst*/, va_list */*ap*/,
+ const struct kwval */*v*/, size_t /*n*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
#include <stdarg.h>
#include <stddef.h>
+#include "keyword.h"
#include "sod-base.h"
/*----- Data structures ---------------------------------------------------*/
nodist_test_SOURCES = test.c test.h
BUILT_SOURCES += $(nodist_test_SOURCES)
+check_PROGRAMS += kwtest
+
+EXTRA_DIST += kwtest.ref
+CLEANFILES += kwtest.out
+check-local:: kwtest kwtest.ref
+ ./kwtest >kwtest.out
+ diff -u $(srcdir)/kwtest.ref kwtest.out
+
###----- That's all, folks --------------------------------------------------
--- /dev/null
+/* -*-c-*-
+ *
+ * Test program for the keyword-argument machinery
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensible Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SOD; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "keyword.h"
+
+static void show_kwval(const char *what, int f, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf(" %s = ", what);
+ if (!f)
+ puts("<unset>");
+ else {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ putchar('\n');
+ }
+}
+
+#define SHOW(mem, fmt) show_kwval(#mem, kw.mem##_suppliedp, fmt, kw.mem)
+
+#define t1_KWSET(_) \
+ _(int, x, 0) \
+ _(double, y, 69.0) \
+ _(const char *, z, "default")
+KWSET_STRUCT(t1);
+static KWSET_PARSEFN(t1)
+static KWCALL void t1(const char *what, KWTAIL)
+{
+ KWPARSE(t1);
+
+ printf("t1: %s\n", what);
+ SHOW(x, "%d");
+ SHOW(y, "%g");
+ SHOW(z, "`%s'");
+}
+
+static KWCALL void t2(const char *what, ...)
+{
+ va_list ap;
+
+ printf("t2: %s\n", what);
+ va_start(ap, what);
+ t1("via t2", KWARGS(K_VALIST(ap)));
+ va_end(ap);
+}
+
+#define t3_KWSET(_) \
+ _(int, q, 0) \
+ t1_KWSET(_)
+KWSET_STRUCT(t3);
+static KWSET_PARSEFN(t3)
+static KWCALL void t3(const char *what, KWTAIL)
+{
+ struct kwval v[KW_COUNT(t1)];
+ size_t n = 0;
+ KWPARSE(t3);
+
+ printf("t3: %s\n", what);
+ SHOW(q, "%c");
+ SHOW(z, "`%s'");
+
+ KW_COPY(t3, t1, kw, v, n);
+ t1("via t3", KWARGS(K_TAB(v, n)
+ K(x, kw.x_suppliedp ? kw.x + 1 : 42)));
+}
+
+/* The @KW_TEST@ machinery from the manpage... */
+#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)
+{
+ if (strcmp(kw, "kw.unknown") == 0) longjmp(kw_test_jmp, 1);
+ else longjmp(kw_test_jmp, 2);
+}
+
+#define KW_TEST(flag, set, call) do { \
+ kw_unkhookfn *oldunk = kw_unkhook; \
+ kw_unkhook = kw_test_unknown; \
+ switch (setjmp(kw_test_jmp)) { \
+ case 0: call; abort(); \
+ case 1: flag = 1; break; \
+ case 2: flag = 0; break; \
+ default: abort(); \
+ } \
+ kw_unkhook = oldunk; \
+} while (0)
+/* END */
+
+int main(void)
+{
+ t1("no args", NO_KWARGS);
+ t1("some args", KWARGS(K(z, "set") K(x, 42)));
+
+ t2("indirection", KWARGS(K(y, 6.283)));
+
+ t3("no args", NO_KWARGS);
+ t3("x initially 19", KWARGS(K(q, 't') K(x, 19) K(z, "boing")));
+
+#define TEST_KWTEST(kw, val) do { \
+ int f_; \
+ KW_TEST(f_, t3, t3(0, KWARGS_TEST(kw, val))); \
+ printf("t3 arg `%s' %s\n", #kw, f_ ? "present" : "absent"); \
+} while (0)
+ TEST_KWTEST(q, '!');
+ TEST_KWTEST(z, "splat");
+ TEST_KWTEST(nope, 0);
+
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+t1: no args
+ x = <unset>
+ y = <unset>
+ z = <unset>
+t1: some args
+ x = 42
+ y = <unset>
+ z = `set'
+t2: indirection
+t1: via t2
+ x = <unset>
+ y = 6.283
+ z = <unset>
+t3: no args
+ q = <unset>
+ z = <unset>
+t1: via t3
+ x = 42
+ y = <unset>
+ z = <unset>
+t3: x initially 19
+ q = t
+ z = `boing'
+t1: via t3
+ x = 20
+ y = <unset>
+ z = `boing'
+t3 arg `q' present
+t3 arg `z' present
+t3 arg `nope' absent