Subject: Robust Argument Passing to C Functions

From: Shlomi Fish <[email-masked]>
Date: Sun, 20 Jul 2003 20:37:31 +0300 (IDT)

If you code a prototype of a function in C like this:

records_t * perfrom_select_query(char * table, char * * fields, char *
where_clause, order_by_t * order_by,....)

Then you may run into the situation where you have to add/remove/modify
the argument list. This way, all of your old code that uses this function
breaks and need to be adapted to the new code. This is clearly not a
scalable solution.

In Perl 5, this is easily solvable using:

my $records_handle =
	perform_select_query(
		'table' => $mytable,
		'fields' => [ @fields ],
		'where' => $where_clause
		...
		);

But, how do we do it in C?

I've seen several solutions for it. The first is to emulate the Perl
behaviour in C, by supplying an associative array of optional key/value
pairs. This is done in Xt/Motif:

<<<
	Arg args[5];
	int n = 0;
    XtSetArg (args[n], XmNselectionLabelString, t); n++;
    XtSetArg (args[n], XmNautoUnmanage, False); n++;
    XtSetArg (args[n], XmNuserData, 0); n++;
    dialog = XmCreatePromptDialog (pb, "notice_popup", args, n);

It can also be done with a string ---> void * hash instead.

Other than that, there's also the possibility of initializing an object,
and calling methods to set its defaults to something else, and then
calling a function to commit the object:

<<<
	sql_select_t * q = sql_select_new();
	sql_select_set_table(q, "mytable");
	sql_select_set_fields(q, myfields);
	sql_select_set_where(q, my_where_clause);

	.
	.
	.
	sql_records_t * records = sql_select_perform(q);

Needless to say, both methods require more code than just passing them to
the function call one by one.

What do you think?

Regards,

	Shlomi Fish

----------------------------------------------------------------------
Shlomi Fish        [email-masked]
Home Page:         http://t2.technion.ac.il/~shlomif/

An apple a day will keep a doctor away. Two apples a day will keep two
doctors away.

	Falk Fish

---

From: "Arik Baratz" <[email-masked]>
Date: Mon, 21 Jul 2003 11:49:27 +0300

> -----Original Message-----
> From: Shlomi Fish [mailto:[email-masked]]
[snip]

> int myfunc(int required_arg,...)
> {
> }
> 
> myfunc(6,"table", mytable,"fields",myfields,"where", my_where_clause,
> NULL);

Yes, it is legal, and no there is no need to determine the number of arguments in advance, provided there is a way to know how many are there, eventually.

What the va_start and va_arg macros do is calculate offsets on the stack where the arguments are. They have no way of knowing otherwise. However, you can opt to simply put a NULL pointer at the end. You do need to have a first known argument, though. If you want to have only string/arg pairs with this solution, you must make sure that the first argument will always exist, even if it is null - and it has to be handled separately:

void myfunc(char* first, ...)
{
	va_list vl;
	va_start (vl,first);
	char* attr = first

	while (attr)
	{
		if (!strcmp(attr,"attrib1"))
		{
			attr1=va_arg(vl,type)
			.
		}
		/* do some more stuff */
		
		attr = va_arg(vl,char*)
	}
}

-- Arik
**********************************************************************
This email and attachments have been scanned for
potential proprietary or sensitive information leakage. 

PortAuthority(TM)  Server 
Keeping Information Inside
Vidius, Inc. 
www.vidius.com
**********************************************************************