Re: representing functions with arguments in an abstract syntax tree

Chris F Clark <cfc@world.std.com>
2 Jan 2004 03:42:56 -0500

          From comp.compilers

Related articles
representing functions with arguments in an abstract syntax tree melkorainur@yahoo.com (2003-12-27)
Re: representing functions with arguments in an abstract syntax tree torek@torek.net (Chris Torek) (2004-01-02)
Re: representing functions with arguments in an abstract syntax tree malcolm@55bank.freeserve.co.uk (Malcolm) (2004-01-02)
Re: representing functions with arguments in an abstract syntax tree cfc@world.std.com (Chris F Clark) (2004-01-02)
Re: representing functions with arguments in an abstract syntax tree jacob@jacob.remcomp.fr (jacob navia) (2004-01-02)
Re: representing functions with arguments in an abstract syntax tree witness@t-online.de (Uli Kusterer) (2004-01-02)
| List of all articles for this month |

From: Chris F Clark <cfc@world.std.com>
Newsgroups: comp.compilers,comp.lang.c
Date: 2 Jan 2004 03:42:56 -0500
Organization: The World Public Access UNIX, Brookline, MA
References: 03-12-142
Keywords: code
Posted-Date: 02 Jan 2004 03:42:56 EST

Melor Ainur asked:


> how do (or is it even possible) I write a generic function pointer
> that can represent all my different functions. some that have
> multiple promotable-arguments (chars, ints) and pointers. and then,
> how do I Pass these functions their arguments?


I suspect others may give a better and more specific answer to your
question, but here is some advice. I believe the solution pattern you
are looking for is the "trampoline" or "bridge".


Instead of putting the C built-in function in your table, you put a
"trampoline" or "bridge" function in your table. That function
matches whatever calling spec you have defined, quote: "currently, my
function_node just stores a function pointer (declared as typedef
(void *)(*fptr)(void*) )", extracts the arugments from the argument
list, quote: "and arglist looks like node {node *next; enum argtype_t
argtype; union {int num; char *str; void* ptr;} body;}" for each of
the arguments the built-in function expects, does any run-time
conversions and error checking necessary, and then calls the desired
built-in function. Upon the return of the built-in function, the code
may do the same steps in reverse to approrpiately deal with the return
value, pass by call-return arguments, etc.


Let us look at a bridge function your specific example, quote:
"TurnOffWireless(interface1, interface2);"


void *TurnOffWireless_Bridge(void *arglist_as_voidptr)
{
node *arglist = (node *)arglist_as_voidptr; // turn into useful type


if ( ! arglist ) {
error("TurnOffWireless: two arguments expected--none found");
}


// say interface1 should be an int (for example)
if ( *arglist->argtype != int_argtype_t_enum ) {
error("TurnOffWireless: 1st argument should be int and was not");
}
int interface1 = arglist->num;


arglist = arglist->next; // now deal with 2nd argument


// say interface2 should be a ptr (for example)
if ( *arglist->argtype != ptr_argtype_t_enum ) {
error("TurnOffWireless: 2nd argument should be ptr and was not");
}
void *interface2 = arglist->ptr;


// call actual function


TurnOffWireless(interface1, interface2);


//do any clean up work need on return side


}




Having written this, you put a pointer to TurnOffWireless_Bridge in
your table of function pointers not a pointer to TurnOffWireless.


------------------------------------------------------------------------


A second look at your question suggests, that you might be asking a
simpler question, quote: "how do (or is it even possible) I write a
generic function pointer that can represent all my different
functions?"


In that, case the answer is the lowly, but all powerful, C cast.
Simply cast your functions to the *type of the table* on putting them
into the table and cast them back to their appropriate function type,
upon extracting them from the table and use.


fptr function_table[] = {
(fptr)&cpu_temp, // cast to insert into table
(fptr)&TurnOffWireless,
// more functions
};


// code here which uses the TurnOffWireless function, slot 2 in table


void (*this_function)(int, void *) = // appropriately typed function call var
(void (*)(int, void *))function_table[2]; // cast to extract


int interface1 = arglist->num; // extract args from arglist as above
void *interface2 = arglist->next->ptr;


(*this_function)(interface1, interface2); // call function


------------------------------------------------------------------------


Note, in both cases, you know all the argument types for your built-in
functions. If you have a function like printf that is "variadic" (or
is it varadic?), you need some way of passing an appropriate argument
list. There are standard "macros" for va_list that handle this when
you call a variadic C function from C code, but to my knowledge there
aren't macros that allow you to build a va_list on-the-fly from "pure
cloth" (i.e. taking your arglist and converting it into a va_list).
However, for many machines the format of a va_list, is simply an array
of pointers to the actual arguments. Thus, you can build such a
structure yourself and pass it in with a reasonable expectation of
success, until you start porting your code.


A common thing to do in the bridge case is to make the bridge know
about calls with some fixed number of arguments (say 10), and then see
how many arguments are in the actual argument list and select with a
case statement the function call that passes those arguments in.




void *Variadic_Bridge(void *arglist_as_voidptr)
{
void *rslt;
node *arglist = (node *)arglist_as_voidptr; // turn into useful type


if ( ! arglist ) {
rslt = Variadic(); // no arguments
return rslt;
}


void *arg1 = arglist->ptr; // assume all args are pointers


arglist = arglist->next;


if ( ! arglist ) {
rslt = Variadic(arg1); // 1 argument
return rslt;
}


void *arg2 = arglist->ptr; // assume all args are pointers


arglist = arglist->next;


if ( ! arglist ) {
rslt = Variadic(arg1, arg2); // 2 arguments
return rslt;
}


// more code follows for 3 .. n arg cases


}


Finally, if you have a truly variadic function, you might again look
at printf for insight. Many compilers implement this via a doprint
function that processes 1 arugment at a time. So, that instead of
building up a call that has the correct number of arguments and
passing that as a whole to printf. The bridge function loops through
the argument list calling doprint on each one.


void *Variadic_Bridge(void *arglist_as_voidptr)
{
void *rslt;


Variadic_start(); // pre processing


node *arglist = (node *)arglist_as_voidptr; // turn into useful type


while ( arglist ) {
Variadic_arg(arglist->ptr); // handle each argument
arglist = arglist->next;
}


rslt = Variadic_end(); // post processing


return rslt;
}


------------------------------------------------------------------------


Note, these are just some simple sketches for what I believe you are
trying to do. In general, whatever you want to do, can probably be
done "efficiently" in C without resorting to assembly language (unless
you need access to some specific "hardware instruction").


Hope this helps,
-Chris


*****************************************************************************
Chris Clark Internet : compres@world.std.com
Compiler Resources, Inc. Web Site : http://world.std.com/~compres
19 Bronte Way #33M voice : (508) 435-5016
Marlboro, MA 01752 USA fax : (508) 251-2347 (24 hours)


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.