1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
/*
Copyright (C) 2016- The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/
/** @file jx_match.h Unwrap JX types
*
* The functions in this header are intended to make JX types more usable
* from C code. All unwrap JX values in some manner, allowing access to
* native C types. For the arrays, the match function can be
* used to destructure its arguments, extracting and type-checking
* components in a single call.
* The match functions for single values return an int indicating the
* result of the match. On match failure, 0 is returned, or 1 on success.
* The match functions can also give the caller a copy of
* the matched value. For heap-allocated types, the caller is responsible
* for free()ing/jx_delete()ing the copied values. In the case that the
* caller requested a heap-allocated copy but malloc() fails, the match
* as a whole will fail. This ensures that it is always safe to dereference
* the copy given back, but might falsely indicate an incorrect type. Code
* that needs to run in out of memory conditions must not request copies.
*
* The common use case is that programs will have some stack allocated
* variables for local use, and would like to read the value in a JX struct.
*
* @code
* double val;
* if (jx_match_double(j, &val)) {
* printf("got value %f\n", val);
* } else {
* printf("not a valid double\n");
* }
* @endcode
*
* Here, the return value is used to check that the given JX struct was
* in fact a double, and to copy the value onto the local function's
* stack. If a function needs to modify the JX structure, it must
* access the struct fields directly.
*
* There is also a matching function to extract multiple positional
* values from an array and validate types in a single call.
* This function has a scanf()-like interface.
* The array matching function takes a JX struct
* and a sequence of item specifications. Each item
* spec includes a JX type and the location of a pointer that will reference
* the extracted value. The match function
* processes each specification in turn, stopping on an invalid spec,
* or NULL, and returning the number of items succesfully matched.
*
* @code
* jx_int_t a;
* double b;
* switch (jx_match_array(j, &a, JX_INTEGER, &b, JX_DOUBLE, NULL)) {
* case 1:
* printf("got int %d\n", a);
* case 2:
* printf("got %d and %f\n", a, b)
* default:
* printf("bad match\n");
* }
* @endcode
*
* It's also possible to match on a position without looking at the
* type of the matched value. The pseudo-type JX_ANY is provided
* for this purpose. Since the type of the value matched by JX_ANY is
* unknown, the caller might want to use the other match functions to
* handle whatever cases they're interested in. The following example
* uses most of the matching functionality.
*
* @code
* struct jx *a;
* struct jx *b;
* if (jx_match_array(j, &a, JX_ARRAY, &b, JX_ANY, NULL) == 2) {
* printf("got JX_ARRAY at %p\n", a);
* if (jx_istype(b, JX_STRING)) {
* printf("no longer supported\n");
* return 0;
* }
* jx_int_t val;
* if (jx_match_integer(b, &val)) {
* return val % 8;
* }
* }
* @endcode
*/
#ifndef JX_MATCH_H
#define JX_MATCH_H
#include "jx.h"
#ifndef JX_ANY
#define JX_ANY -1
#endif
/*
* Macro to test if we're using a GNU C compiler of a specific vintage
* or later, for e.g. features that appeared in a particular version
* of GNU C. Usage:
*
* #if __GNUC_PREREQ__(major, minor)
* ...cool feature...
* #else
* ...delete feature...
* #endif
*/
#ifndef __GNUC_PREREQ__
#ifdef __GNUC__
#define __GNUC_PREREQ__(x, y) \
((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \
(__GNUC__ > (x)))
#else
#define __GNUC_PREREQ__(x, y) 0
#endif
#endif
#ifndef __wur
#if __GNUC_PREREQ__(3, 4)
#define __wur __attribute__((warn_unused_result))
#else
#define __wur
#endif
#endif
#ifndef __sentinel
#if __GNUC_PREREQ__(4, 0)
#define __sentinel __attribute__((sentinel))
#else
#define __sentinel
#endif
#endif
/** Unwrap a boolean value.
* @param j The JX structure to match.
* @param v An address to copy the boolean value to, or NULL if copying
* the value is unnecessary.
* @return A truth value indicating the result of the match.
*/
int jx_match_boolean(struct jx *j, int *v) __wur;
/** Unwrap an integer value.
* @param j The JX structure to match.
* @param v An address to copy the integer value to, or NULL if copying
* the value is unnecessary.
* @return A truth value indicating the result of the match.
*/
int jx_match_integer(struct jx *j, jx_int_t *v) __wur;
/** Unwrap a double value.
* @param j The JX structure to match.
* @param v An address to copy the double value to, or NULL if copying
* the value is unnecessary.
* @return A truth value indicating the result of the match.
*/
int jx_match_double(struct jx *j, double *v) __wur;
/** Unwrap a string value.
* Since C strings are just pointers to char arrays, the interface
* here is a little awkward. If the caller has a stack-allocated
* (char *), this function can allocate a copy of the string value
* and store the address in the caller's pointer variable, e.g.
*
* @code
* char *val;
* if (jx_match_string(j, &val)) {
* printf("got value %s\n", val);
* }
* @endcode
*
* @param j The JX structure to match.
* @param v The address of a (char *) in which to store the address of
* the newly malloc()ed string, or NULL if copying the value is unnecessary.
* @return A truth value indicating the result of the match.
*/
int jx_match_string(struct jx *j, char **v) __wur;
/** Unwrap a symbol value.
* This function accesses the symbol name as a string.
* See @ref jx_match_string for details.
*
* @param j The JX structure to match.
* @param v The address of a (char *) in which to store the address of
* the newly malloc()ed symbol name, or NULL
* if copying the value is unnecessary.
* @return A truth value indicating the result of the match.
*/
int jx_match_symbol(struct jx *j, char **v) __wur;
/** Destructure an array.
* This function accepts an arbitrary number of positional specifications
* to attempt to match. Each specification is of the form
*
* `<address>`, `<jx type>`
*
* where `<jx type>` is JX_INTEGER, JX_ANY, etc. and `<address>` is the address
* to store the matched value. The last argument must be NULL to mark the
* end of the specifications. The specifications will be matched
* in the order given, and matching ends on the first failure. If the JX
* value passed in was not an array, this is considered a failure before
* any matches succeed, so 0 is returned.
* @param j The JX structure to match.
* @return The number of elements successfully matched.
*/
int jx_match_array(struct jx *j, ...) __wur __sentinel;
#endif
|