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