2Copyright (c) 2005--2013 by Ben Klemens. Licensed under the GPLv2; see COPYING. */
3
2255/* optionaldetails Implementation of optional arguments [this section ignored by doxygen]
2256Optional and named arguments are among the most commonly commented-on features of Apophenia, so this page goes into full detail about the implementation.
2257
2258To use these features, see the all-you-really-need summary at the \ref designated
2259page. For a background and rationale, see the blog entry at http://modelingwithdata.org/arch/00000022.htm .
2260
2261I'll assume you've read both links before continuing.
2262
2263OK, 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.
2264
2265There are three components to the process of generating optional arguments as implemented here:
2266\li Produce a \c struct whose elements match the arguments to the function.
2267\li Write a wrapper function that takes in the struct, unpacks it, and calls the original function.
2268\li Write a macro that makes the user think the wrapper function is the real thing.
2269
2270None of these steps are really rocket science, but there is a huge amount of redundancy.
2271Apophenia 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.
2272
2273We'll begin with the C-standard header file:
2274\code
2275#ifdef APOP_NO_VARIADIC
2276 void apop_vector_increment(gsl_vector * v, int i, double amt);
2277#else
2278 void apop_vector_increment_base(gsl_vector * v, int i, double amt);
2279 apop_varad_declare(void, apop_vector_increment, gsl_vector * v; int i; double amt);
2308That gives us part three: a macro that lets the user think that they are
2309making a typical function call with a set of arguments, but wraps what
2310they type into a struct.
2311
2312Now 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.
2313
2314\code
2315\#ifdef APOP_NO_VARIADIC
2316 void apop_vector_increment(gsl_vector * v, int i, double amt){
2338The function with this header thus takes in a single struct, and for every variable, there is a line like
2339\code
2340 double apop_varad_var(amt, 1);
2341\endcode
2342which simply expands to:
2343\code
2344 double amt = varad_in.amt ? varad_in.amt : 1;
2345\endcode
2346Thus, the macro declares each not-in-struct variable, and so there will need to be
2347one such declaration line for each argument. Apart from requiring declarations, you
2348can be creative: include sanity checks, post-vary the variables of the inputs, unpack
2349without the macro, and so on. That is, this parent function does all of the bookkeeping,
2350checking, and introductory shunting, so the base function can do the math. Finally,
2351the introductory section will call the base function.
2352
2353The setup goes out of its way to leave the \c _base function in the public namespace,
2354so that those who would prefer speed to bounds-checking can simply call that function
2355directly, using standard notation. You could eliminate this feature by merging
2356the two functions.
2357
2358
2359<b>The m4 script</b>
2360
2361The 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.
2362
2363Thus, 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:
2364
2365\code
2366//header file
2367APOP_VAR_DECLARE void apop_vector_increment(gsl_vector * v, int i, double amt);
2368
2369//code file
2370APOP_VAR_HEAD void apop_vector_increment(gsl_vector * v, int i, double amt){
2371 gsl_vector * apop_varad_var(v, NULL);
2372 Apop_stopif(!v, return, 0, "You sent me a NULL vector.");
2373 int apop_varad_var(i, 0);
2374 double apop_varad_var(amt, 1);
2375APOP_VAR_END_HEAD
2376 v->data[i * v->stride] += amt;
2377}
2378\endcode
2379
2380It 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.
2381
2382The m4 script inserts a <tt>return function_base(...)</tt> at the end of the header
2383function, so you don't have to. If you want to call the function before the last line, you
2384can do so explicitly, as in the expansion above, and add a bare <tt>return;</tt> to
2385guarantee that the call to the base function that the m4 script will insert won't ever be
2386reached.
2387
2388One 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.,
2389
2390\code
2391APOP_VAR_DECLARE apop_data * f_of_f(apop_data *in, void *param, int n, double (*fn_d)(double ! void * !int));
2392\endcode
2393
2394m4 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