Josh Dybnis on 10 Jun 2003 22:31:01 -0000 |
[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]
Re: [ALACPP] Meeting |
> Tomorrow, 12pm, noon, in Pasadena. We will discuss the ever-popular > Functors chapter (#5), and puzzle through Josh's creation. I'm out of town, so I won't be there to explain it. So instead here's a little bit of a guide. My functors have three "virtual methods," execute, clone, and free. A functor is represented by a struct containing three function pointers (one for each virtual method), followed optionally by any other data needed for to support the operations (i.e. bound parameters, the size of the struct, etc.). For example, the macro expansion of the type declaration of a functor called "Foo" with an underlying prototype of "int Foo (int)" looks something like this: typedef struct Foo *Foo; struct Foo { int (*execute)(Foo, int); void (*free)(Foo); Foo (*clone)(Foo); size_t size; }; You can see the first argument of each operation is the functor itself, this is typical when doing OOP in C. A macro expansion of EXECUTE_FUNCTOR(f, x) would look like this: Foo f; int x; .... f->execute(f, x); So far this is all pretty straightforward for functors with only one argument. But the preprocessor mumbo-jumbo comes into play when we want to handle functors with an arbitrary number of arguments (actually up to 15). We need facilities to manipulate lists of types, analogous to Loki::Typelists. The macros SKIP(), TAKE(), and MAP() are designed to manipulate lists of types. SKIP(n, ...) has a number and a list as input, and returns the list without the first n elements. TAKE(n, ...) has a number and a list as input and returns the first n elements of the list. MAP() the workhorse. MAP() is a higher-order macro, thats a macro that has the name of another macro as an argument. MAP(f, n, ...) applies the macro f to each of the n remaining arguments, one at a time, while concatenating the results together. This is possible because the rules the preprocessor follows for expanding macros. Since the f macro takes arguments itself, it will not be expanded by the preprocessor until it is placed in-front of a pair of parenthesis. In effect delaying the evaluation of f until we apply it to an argument. There is another preprocessing trick we use to get primitive flow control in a macro. Assume some macro has a parameter named n that is always an integer. Now this macro calls another macro named f with the expression f##n(). The concatenate operator (##) combines two tokens to form one token. So if f is SKIP, and n is 3, then f##n(x) expands to SKIP3(x), which is then further expanded by the preprocessor according to the definition of SKIP3(). To recap, we have selected a macro from an indexed family of macros, based on a parameter passed in by the caller. This is the preprocessor version of a switch statement, or a function lookup table. -Josh Dybnis _______________________________________________ alacpp mailing list alacpp@xxxxxxxxxxx http://lists.ellipsis.cx/mailman/listinfo/alacpp