X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/e520bc2484f96c38991fb8d3b49cd9a3b2410842..9e91c8e7b5fcdeb6389ac7ccbcd9c77348c4493a:/lib/keyword.h diff --git a/lib/keyword.h b/lib/keyword.h new file mode 100644 index 0000000..1fa3e56 --- /dev/null +++ b/lib/keyword.h @@ -0,0 +1,583 @@ +/* -*-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 +#include +#include + +/*----- 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