Patterns in static

Apophenia

documentation.h
1 /* Apophenia's narrative documentation
2 Copyright (c) 2005--2013 by Ben Klemens. Licensed under the GPLv2; see COPYING. */
3 
2274 /* optionaldetails Implementation of optional arguments [this section ignored by doxygen]
2275 Optional and named arguments are among the most commonly commented-on features of Apophenia, so this page goes into full detail about the implementation.
2276 
2277 To use these features, see the all-you-really-need summary at the \ref designated
2278 page. For a background and rationale, see the blog entry at http://modelingwithdata.org/arch/00000022.htm .
2279 
2280 I'll assume you've read both links before continuing.
2281 
2282 OK, now that you've read the how-to-use and the discussion of how optional and named arguments can be constructed in C, this page will show how they are done in Apophenia. The level of details should be sufficient to implement them in your own code if you so desire.
2283 
2284 There are three components to the process of generating optional arguments as implemented here:
2285 \li Produce a \c struct whose elements match the arguments to the function.
2286 \li Write a wrapper function that takes in the struct, unpacks it, and calls the original function.
2287 \li Write a macro that makes the user think the wrapper function is the real thing.
2288 
2289 None of these steps are really rocket science, but there is a huge amount of redundancy.
2290 Apophenia includes some macros that reduce the boilerplate redundancy significantly. There are two layers: the C-standard code, and the script that produces the C-standard code.
2291 
2292 We'll begin with the C-standard header file:
2293 \code
2294 #ifdef APOP_NO_VARIADIC
2295  void apop_vector_increment(gsl_vector * v, int i, double amt);
2296 #else
2297  void apop_vector_increment_base(gsl_vector * v, int i, double amt);
2298  apop_varad_declare(void, apop_vector_increment, gsl_vector * v; int i; double amt);
2299 #define apop_vector_increment(...) apop_varad_link(apop_vector_increment, __VA_ARGS__)
2300 #endif
2301 \endcode
2302 
2303 First, there is an if/else that allows the system to degrade gracefully
2304 if you are sending C code to a parser like swig, whose goals differ
2305 too much from straight C compilation for this to work. Set \c
2306 APOP_NO_VARIADIC to produce a plain function with no variadic support.
2307 
2308 Else, we begin the above steps. The \c apop_varad_declare line expands to the following:
2309 
2310 \code
2311 typedef struct {
2312  gsl_vector * v; int i; double amt ;
2313 } variadic_type_apop_vector_increment;
2314 
2315 void variadic_apop_vector_increment(variadic_type_apop_vector_increment varad_in);
2316  \endcode
2317 
2318 So there's the ad-hoc struct and the declaration for the wrapper
2319 function. Notice how the arguments to the macro had semicolons, like a
2320 struct declaration, rather than commas, because the macro does indeed
2321 wrap the arguments into a struct.
2322 
2323  Here is what the \c apop_varad_link would expand to:
2324  \code
2325 #define apop_vector_increment(...) variadic_apop_increment_base((variadic_type_apop_vector_increment) {__VA_ARGS__})
2326  \endcode
2327 That gives us part three: a macro that lets the user think that they are
2328 making a typical function call with a set of arguments, but wraps what
2329 they type into a struct.
2330 
2331 Now for the code file where the function is declared. Again, there is is an \c APOP_NO_VARIADIC wrapper. Inside the interesting part, we find the wrapper function to unpack the struct that comes in.
2332 
2333 \code
2334 \#ifdef APOP_NO_VARIADIC
2335  void apop_vector_increment(gsl_vector * v, int i, double amt){
2336 \#else
2337 apop_varad_head( void , apop_vector_increment){
2338  gsl_vector * apop_varad_var(v, NULL);
2339  Apop_stopif(!v, return, 0, "You sent me a NULL vector.");
2340  int apop_varad_var(i, 0);
2341  double apop_varad_var(amt, 1);
2342  apop_vector_increment_base(v, i, amt);
2343 }
2344 
2345  void apop_vector_increment_base(gsl_vector * v, int i, double amt){
2346 #endif
2347  v->data[i * v->stride] += amt;
2348 }
2349 \endcode
2350 
2351 The
2352 \c apop_varad_head macro reduces redundancy, and will expand to
2353 \code
2354 void variadic_apop_vector_increment (variadic_type_variadic_apop_vector_increment varad_in)
2355 \endcode
2356 
2357 The function with this header thus takes in a single struct, and for every variable, there is a line like
2358 \code
2359  double apop_varad_var(amt, 1);
2360 \endcode
2361 which simply expands to:
2362 \code
2363  double amt = varad_in.amt ? varad_in.amt : 1;
2364 \endcode
2365 Thus, the macro declares each not-in-struct variable, and so there will need to be
2366 one such declaration line for each argument. Apart from requiring declarations, you
2367 can be creative: include sanity checks, post-vary the variables of the inputs, unpack
2368 without the macro, and so on. That is, this parent function does all of the bookkeeping,
2369 checking, and introductory shunting, so the base function can do the math. Finally,
2370 the introductory section will call the base function.
2371 
2372 The setup goes out of its way to leave the \c _base function in the public namespace,
2373 so that those who would prefer speed to bounds-checking can simply call that function
2374 directly, using standard notation. You could eliminate this feature by merging
2375 the two functions.
2376 
2377 
2378 <b>The m4 script</b>
2379 
2380 The above is all you need to make this work: the varad.h file, and the above structures. But there is still a lot of redundancy, which can't be eliminated by the plain C preprocessor.
2381 
2382 Thus, in Apophenia's code base (the one you'll get from checking out the git repository, not the gzipped distribution that has already been post-processed) you will find a pre-preprocessing script that converts a few markers to the above form. Here is the code that will expand to the above C-standard code:
2383 
2384 \code
2385 //header file
2386 APOP_VAR_DECLARE void apop_vector_increment(gsl_vector * v, int i, double amt);
2387 
2388 //code file
2389 APOP_VAR_HEAD void apop_vector_increment(gsl_vector * v, int i, double amt){
2390  gsl_vector * apop_varad_var(v, NULL);
2391  Apop_stopif(!v, return, 0, "You sent me a NULL vector.");
2392  int apop_varad_var(i, 0);
2393  double apop_varad_var(amt, 1);
2394 APOP_VAR_END_HEAD
2395  v->data[i * v->stride] += amt;
2396 }
2397 \endcode
2398 
2399 It is obviously much shorter. The declaration line is actually a C-standard declaration with the \c APOP_VAR_DECLARE preface, so you don't have to remember when to use semicolons. The function itself looks like a single function, but there is again a marker before the declaration line, and the introductory material is separated from the main matter by the \c APOP_VAR_END_HEAD line. Done right, drawing a line between the introductory checks or initializations and the main function can improve readability.
2400 
2401 The m4 script inserts a <tt>return function_base(...)</tt> at the end of the header
2402 function, so you don't have to. If you want to call the function before the last line, you
2403 can do so explicitly, as in the expansion above, and add a bare <tt>return;</tt> to
2404 guarantee that the call to the base function that the m4 script will insert won't ever be
2405 reached.
2406 
2407 One final detail: it is valid to have types with commas in them---function arguments. Because commas get turned to semicolons, and m4 isn't a real parser, there is an exception built in: you will have to replace commas with exclamation marks in the header file (only). E.g.,
2408 
2409 \code
2410 APOP_VAR_DECLARE apop_data * f_of_f(apop_data *in, void *param, int n, double (*fn_d)(double ! void * !int));
2411 \endcode
2412 
2413 m4 is POSIX standard, so even if you can't read the script, you have the program needed to run it. For example, if you name it \c prep_variadics.m4, then run
2414 \code
2415 m4 prep_variadics.m4 myfile.m4.c > myfile.c
2416 \endcode
2417 */
2418 
2419 
Autogenerated by doxygen (Debian ).