This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Unnamed functions, functors or, more formally, function literals



I believe this is a feature most C programmers dream of (not mentioning
many friends of mine and me myself). This one has already been proposed 
by several people (including the man that implemented nested functions 
in GCC compiler and Paul Long with his proposal for C language
standardisation 
comitee). It`s a pity, but I haven`t seen a compiler that implements it.

I now try to show how valuable this feature is and show what I exactly
mean
under unnamed function. First a classical example:


char *mas[10];

int compare(void *a1, void *a2) {
	return strcmp(*(char **)a1, *(char **)a2);
}

int main(void) {
	//...
	{
		qsort(mas, NELEMS(mas), sizeof mas[0], compare);
	}
	//...
}

could better be written as:

int main(void) {
	//...
	{
		qsort(mas, NELEMS(mas), sizeof mas[0], 
		      (int (void *a1, void *a2) 
		            {return strcmp(*(char **)a1, *(char
**)a2);}));
	}
	//...
}


Unnamed functions differ with nested functions mostly with their scope.
Nested functions can refer to local variables in containing scopes, but
unnamed ones can only refer to globals (receiving locals in arguments).
Unnamed function can be defined in global binding level as well.

Above said allows us to write more interesting examples - let`s try to 
write a symbol table driver "class" in C:

struct driver {
	void *data;
	void (*init)(void);
	void (*close)(void);
	void (*insert)(char *);
	void (*delete)(char *);
	int (*lookup)(char *);
};

We "implement" it like this:

struct driver dummy = {
	.data = NULL,
	.init = (void () {
		data = xmalloc(TABLE_SIZE);
		// or something like this
	}),
	.close = (void () {
		free(data);
		// other like this
	}),
	.insert = (void (char *s) {
		// ...
	}),
	.delete = (void (char *s) {
		// ...
	}),
	.lookup = (int (char *s) {
		// ...
	})
};

Unnamed functions can be called right away 
as well as initializing a pointer to function:

#define compute (int (int a, int b) \
	{while (a--) b += foo(b); return b;})

int main(void) {
	int x, y;
	int res = compute(x, y);
	int (*p)(int, int) = compute;
	// ...
}


Actually, I already hacked my GCC compiler to support it. I will shortly 
describe changes that need to be done to integrate this feature.

At first, the grammar:

primary_expr:
	// ...
	( typename compound_stmt )
	;

typename should resolve to function type.
	
There is a definition of cast_expr, that forbids placing anything other in
place of typename, like storage class modifiers.

cast_expr:
	// ...
	( typename ) some_expr
	;

But storage class seems useless anyway, because static and extern are of
no 
meaning here, and inline may not be needed (or may be made default, which
mean "inline direct calls").

This introduced 3 shift/reduce conflicts, that were resolved by default 
correctly. In
	struct tag {}
	enum tag {}
	union tag {}
{ resolve to shift to struct/enum/union declarations.

That is all about grammar.


The semantic processing of unnamed function definition is conceptually
simple.
As I said, the declaration level of unnamed functions is global, so,
generally,
what should be done is to 

* save current status of translation (if we were translating a function
body), 

* switch to global bindings level, 

* build a tree for unnamed function`s body, 

* generate assembly code on completion of translation (under some label
.Lxx),

* assign a declaration for the function to $$, a primary_expr.

* restore status of compilation

Now you have it.


I basically did just that. It works, but it would be great if I were a
little
bit more experienced in GCC internals.

Now I will ask you why this extremely useful un fully compatible with the 
rest of the grammar feature is not there yet? 

If you think that this feature could be incorporated in GCC C compiler, 
i can show you my code, but, basically, the implementation seems to be 
rather easy.





Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]