Keyword parameters


J

James Harris

C function calls have positional parameters but in some cases keyword
parameters would be useful such as with a sparse argument list. Is there any
preferred way in C to implement something close to keyword parameters?

James
 
Ad

Advertisements

S

Stefan Ram

James Harris said:
C function calls have positional parameters but in some cases keyword
parameters would be useful such as with a sparse argument list. Is there any
preferred way in C to implement something close to keyword parameters?

Instead of

srand( 2227 );

use

{ unsigned int const seed = 2227; srand( seed ); }

. Or, - more elaborated - instead of

OpenWindow( 0, 0, 40, 40, 0, 1, "simple", WINDOWCLOSE |
SMART_REFRESH | ACTIVATE | WINDOWSIZING | WINDOWDRAG |
WINDOWDEPTH | NOCAREREFRESH, CLOSEWINDOW, WBENCHSCREEN, NULL,
NULL, NULL, NULL, 0, 0, 0, 0 );

use an interface that can be called like

struct NewWindow NewWindow;

NewWindow.LeftEdge = 0;
NewWindow.TopEdge = 0;
NewWindow.Width = 40;
NewWindow.Height = 40;
NewWindow.DetailPen = 0;
NewWindow.BlockPen = 1;
NewWindow.Title = "simple";
NewWindow.Flags = WINDOWCLOSE | SMART_REFRESH | ACTIVATE |
WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH |
NOCAREREFRESH;
NewWindow.IDCMPFlags = CLOSEWINDOW;
NewWindow.Type = WBENCHSCREEN;
NewWindow.FirstGadget = NULL;
NewWindow.CheckMark = NULL;
NewWindow.Screen = NULL;
NewWindow.BitMap = NULL;
NewWindow.MinWidth = 0;
NewWindow.MinHeight = 0;
NewWindow.MaxWidth = 0;
NewWindow.MaxHeight = 0;

OpenWindow( &NewWindow );
 
B

BartC

OpenWindow( 0, 0, 40, 40, 0, 1, "simple", WINDOWCLOSE |
SMART_REFRESH | ACTIVATE | WINDOWSIZING | WINDOWDRAG |
WINDOWDEPTH | NOCAREREFRESH, CLOSEWINDOW, WBENCHSCREEN, NULL,
NULL, NULL, NULL, 0, 0, 0, 0 );

use an interface that can be called like

struct NewWindow NewWindow;

NewWindow.LeftEdge = 0;
NewWindow.TopEdge = 0;
NewWindow.Width = 40;
NewWindow.Height = 40;
NewWindow.DetailPen = 0;
NewWindow.BlockPen = 1;
NewWindow.Title = "simple";
NewWindow.Flags = WINDOWCLOSE | SMART_REFRESH | ACTIVATE |
WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH |
NOCAREREFRESH;
NewWindow.IDCMPFlags = CLOSEWINDOW;
NewWindow.Type = WBENCHSCREEN;
NewWindow.FirstGadget = NULL;
NewWindow.CheckMark = NULL;
NewWindow.Screen = NULL;
NewWindow.BitMap = NULL;
NewWindow.MinWidth = 0;
NewWindow.MinHeight = 0;
NewWindow.MaxWidth = 0;
NewWindow.MaxHeight = 0;

OpenWindow( &NewWindow );

That's only practical when you can design OpenWindow() yourself. You can't
really ask MS to rewrite CreateWindow(), for example, to your specification!

However MS do like to use this technique, of having to create and populate a
struct to be passed as a parameter, in many of their interfaces, but it's
even more of a pain that having to supply a dozen positional parameters.

Keyword parameters would let you write your example as:

OpenWindow(Width=40,Height=40, Blockpen=1, Title="Simple",
IDCMFlags=CLOSEWINDOW, Type=WBENCHSCREEN,
Flags=WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWSIZING |
WINDOWDRAG | WINDOWDEPTH | NOCAREREFRESH);

This requires that parameters not specified will be set to some default
value (eg. zero), otherwise keyword parameters are not so useful.

I can't think of how to do this tidily in C without language support.
 
J

James Kuyper

C function calls have positional parameters but in some cases keyword
parameters would be useful such as with a sparse argument list. Is there any
preferred way in C to implement something close to keyword parameters?

C99 added two features named compound literals and designated
initializers, that can be combined to gain essentially the same benefits
that could be provided by keyword parameters:

"keyparamfunc.h":

struct func_params{
// Many parameters that you don't want to set.
char *name;
int age;
double salary;
};
int keyparamfunc(struct func_params in);


Some other module:

#include "keyparamfunc.h"

int main(void)
{
int j = keyparamfunc((struct func_params)
{.salary=45933.25, .name="John Doe", .age=45});


}

The code in main is essentially equivalent to:

struct func_params temp={0};
temp.salary = 45933.25;
temp.name = "John Doe";
temp.age = 45;
int i = keyparamfunc(temp);

Using compound literals is clumsier than using true keyword parameters,
in that you must explicitly specify the struct type in the argument list
of the function call.
 
B

Ben Bacarisse

James Harris said:
C function calls have positional parameters but in some cases keyword
parameters would be useful such as with a sparse argument list. Is there any
preferred way in C to implement something close to keyword parameters?

I don't know of a preferred way. There probably isn't one.

But if passing a structure appeals as an almost but not quite
approximation to something that this not entirely unlike named
parameters, it is useful to know about C's compound literals and
designated initialisers:

f((f_args){ .size = 42, .type = "blue" });

Every such function needs a corresponding struct (here I've assumed a
typedef of it).

I've never done this enough to know if it's worth it. I'm posting
simply because these parts of C are little known.
 
M

Malcolm McLean

C function calls have positional parameters but in some cases keyword
parameters would be useful such as with a sparse argument list. Is there any
preferred way in C to implement something close to keyword parameters?
Presumably you would write a list of string / argument pairs and pass them
to the varargs engine, with a terminating nul.

But I've never actually seen a C function written like that. You lose all the
compile time type checking, of course.
 
Ad

Advertisements

R

Richard Tobin

Presumably you would write a list of string / argument pairs and pass them
to the varargs engine, with a terminating nul.

But I've never actually seen a C function written like that. You lose all the
compile time type checking, of course.

Several graphics libraries work like that, as they tend to have
innumerable optional arguments. The first I remember was Suntools on
1980s Sun Workstations. Rather than strings it used #defined or
enumerated constants, and IIRC the constants contained bitfields
identifying the type required for the corresponding value, so that
they could be passed on without having to know all the possible
arguments.

-- Richard
 
J

James Harris

Malcolm McLean said:
Presumably you would write a list of string / argument pairs and pass them
to the varargs engine, with a terminating nul.

That's an option I thought of but using integer constants instead of
strings. (I presume that by nul you mean a null pointer.)

So where I understand you to mean

f(pos0, pos1, NULL); /* Only positional, no keyword parms */
f(pos0, pos1, "keyword0", kparm0, "keyword1", kparm1, NULL);

I would have had

f(pos0, pos1, 0); /* Only positional, no keyword parms */
f(pos0, pos1, KWORD0, kparm0, KWORD1, kparm1, 0);
But I've never actually seen a C function written like that.
OK.

You lose all the
compile time type checking, of course.

Good point.

James
 
N

Noob

f(pos0, pos1, NULL); /* Only positional, no keyword parms */
f(pos0, pos1, "keyword0", kparm0, "keyword1", kparm1, NULL);

Going on a tangent.

When passing NULL to variadic functions, NULL must be cast to
(void *) because some platforms define NULL as (unadorned) 0,
thus it would be considered an int, in that context.

Regards.
 
M

Malcolm McLean

That's an option I thought of but using integer constants instead of
strings. (I presume that by nul you mean a null pointer.)
Actually it's probably better to use the convention that the empty string
is the terminator, to avoid the NULL casting problem that Noob raised.

I'd recommend using strings rather than defined integer constants. A
maintaining programmer will be scratching his head wondering exactly
what the integers mean and where they are defined. Strings are more
intuitive, and they don't pollute namespace. If you're passing huge numbers
of optional parameters, it's unlikely that the function is going to be
a time critical call.
 
J

James Kuyper

Going on a tangent.

When passing NULL to variadic functions, NULL must be cast to
(void *) because some platforms define NULL as (unadorned) 0,
thus it would be considered an int, in that context.

In principle, NULL could expand to an expression of any integer type,
not just int. Of course, there is no obvious reason for it to expand
into anything other than either 0 or (void*)0).
 
Ad

Advertisements

S

Stefan Ram

James Harris said:
f(pos0, pos1, "keyword0", kparm0, "keyword1", kparm1, NULL);

One can sometimes transform this into

keyword0( kparm0 );
keyword1( kparm1 );
f( pos0, pos1 );

. The two keyword functions belong to the same library
as f and prepare settings for the subsequent f call.

An example in the standard library is the preparation
of a rand() call by an srand() call.
 
M

Malcolm McLean

One can sometimes transform this into

keyword0( kparm0 );
keyword1( kparm1 );
f( pos0, pos1 );

. The two keyword functions belong to the same library
as f and prepare settings for the subsequent f call.

An example in the standard library is the preparation
of a rand() call by an srand() call.
That's common for graphics routines.
But often you then need to be able to push and restore state to
avoid knock-on effects for subsequent calls.
 
K

Keith Thompson

Noob said:
Going on a tangent.

When passing NULL to variadic functions, NULL must be cast to
(void *) because some platforms define NULL as (unadorned) 0,
thus it would be considered an int, in that context.

NULL should be cast to whatever pointer type the function expects.
Often that's going to be void*, but in this case it appears that the
function expects char*.

f(pos0, pos1, (char*)NULL); /* Only positional, no keyword parms */
f(pos0, pos1, "keyword0", kparm0, "keyword1", kparm1, (char*)NULL);
 
K

Keith Thompson

James Kuyper said:
In principle, NULL could expand to an expression of any integer type,
not just int. Of course, there is no obvious reason for it to expand
into anything other than either 0 or (void*)0).

You missed a parenthesis on ((void*)0).

Incidentally, I've just thought of another peculiar but legal way to
define NULL:

#define NULL ('8'/'9')
 
J

James Harris

....
Keyword parameters would let you write your example as:

OpenWindow(Width=40,Height=40, Blockpen=1, Title="Simple",
IDCMFlags=CLOSEWINDOW, Type=WBENCHSCREEN,
Flags=WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWSIZING |
WINDOWDRAG | WINDOWDEPTH | NOCAREREFRESH);

This requires that parameters not specified will be set to some default
value (eg. zero), otherwise keyword parameters are not so useful.

I can't think of how to do this tidily in C without language support.

No solution would be perfect. How about

OpenWindow(
WIN_Width, 40,
WIN_Height, 40,
WIN_BlockPen, 1,
WIN_Title, "Simple",
WIN_IDCMFlags, CLOSEWINDOW,
WIN_Type, WBENCHSCREEN,
WIN_Flags, WINDOWCLOSE | SMART_REFRESH | ACTIVATE |
WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH |
NOCAREREFRESH,
0
);

Genuine question. Is that good/bad/mediocre?

James
 
Ad

Advertisements

J

James Harris

Malcolm McLean said:
Actually it's probably better to use the convention that the empty string
is the terminator, to avoid the NULL casting problem that Noob raised.

I'd recommend using strings rather than defined integer constants. A
maintaining programmer will be scratching his head wondering exactly
what the integers mean and where they are defined. Strings are more
intuitive, and they don't pollute namespace.

Set against that, names would be
* fast
* idiomatic
* definable as header constants or as externs
* checkable at compile or link time.

I can see some value in using strings but, as well as being slower to match,
errors in string parms might not show up until run time.
If you're passing huge numbers
of optional parameters, it's unlikely that the function is going to be
a time critical call.

Much of the time a keyword-based list of parms would be used sparsely so
fewer parameters would be passed than those available.

James
 
J

James Harris

Stefan Ram said:
One can sometimes transform this into

keyword0( kparm0 );
keyword1( kparm1 );
f( pos0, pos1 );

. The two keyword functions belong to the same library
as f and prepare settings for the subsequent f call.

I don't think I understand that. Do you mean that the keyword parameters
would be stored in globals?
An example in the standard library is the preparation
of a rand() call by an srand() call.

OK.

James
 
M

Malcolm McLean

Set against that, names would be

* fast
You can't use strings for speed-critical code, agreed.
* idiomatic
C programmers do expect runtime micro-efficiency. But you're already being non-
idiomatic by introducing named arguments.
* definable as header constants or as externs
That's largely bad.

You can just use "x" or "N" as a string. With integers and bit masks, it's got to be
QLIB_CURSOR_X.
* checkable at compile or link time.
That's a slight advantage. You can't pass "X" or "x " accidentally. But mostly those bugs
are easily caught when you parse the name / argument pairs.
 
Ad

Advertisements

B

BartC

James Harris said:
No solution would be perfect.

Well, when they added designated initialisers to the language, they could
have thrown in keyword parameters too, as it's a similar sort of thing. I
think the latter would be more useful.
How about

OpenWindow(
WIN_Width, 40,
WIN_Height, 40,
WIN_BlockPen, 1,
WIN_Title, "Simple",
WIN_IDCMFlags, CLOSEWINDOW,
WIN_Type, WBENCHSCREEN,
WIN_Flags, WINDOWCLOSE | SMART_REFRESH | ACTIVATE |
WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH |
NOCAREREFRESH,
0
);

Genuine question. Is that good/bad/mediocre?

For C it's good. I've used the same approach at some time or other (often
when needing to provide some options out of a large selection, not
necessarily when using parameters).

The disadvantages compared with proper keyword parameters:

* You need to use the mechanisms for varargs, and specially designed
function interfaces (you can't apply directly to existing functions)

* There are overheads with processing the list of pairs, and assembling the
parameters properly.

* There is no compile-time checking of parameter names and argument types,
or whether you've passed the same parameter twice for example.

* You can't mix positional and keyword parameters (except I think by
specifying a fixed number of positional parameters, although that might
depend on how varargs works, and the order they are pushed).

(Keyword parameters can be applied any function, including existing library
functions (they just need named parameters, but that can be arranged); there
are no extra overheads involved; there would be compile-time checking; and
can have positional parameters as well. There would usually also be a
mechanism to specify a default value.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top