*generic* pointer to function(s)?

J

juerg.lemke

Hi everyone

I am interested in having multiple functions with different prototypes
and deciding, by setting a pointer, which of them to use later in the
program.

Eg:

int f1(void);
char* f2(int);
/* ... many more of these to choose from */

/* ? correct declaration of generic_ptr ? */

if (/*...*/)
generic_ptr = f1;
else
generic_ptr = f2;

generic_ptr(/* using correct args */)

How should I define generic_ptr? Is (void*) ok? It seems to work on my
machine, but I would like to know if this is standard/portable.

Thank you for any help

Juergen
 
P

Peter Nilsson

Hi everyone

I am interested in having multiple functions with different
prototypes and deciding, by setting a pointer, which of
them to use later in the program.

Eg:

int f1(void);
char* f2(int);
/* ... many more of these to choose from */

/* ? correct declaration of generic_ptr ? */

if (/*...*/)
generic_ptr = f1;
else
generic_ptr = f2;

generic_ptr(/* using correct args */)

How should I define generic_ptr?

You can't.
Is (void*) ok?
No.

It seems to work on my machine,

You got unlucky.
but I would like to know if this is standard/portable.

It isn't. The most common generic function pointer type
is void (*)(), though that won't cater for variadic
functions. Moreover, you generally need to cast to
and from the original function pointer in order to use
it.

To avoid the casting, it's much more common to make all
your functions have the same prototype. Another
alternative is to use a union type.
 
E

Eric Sosman

Hi everyone

I am interested in having multiple functions with different prototypes
and deciding, by setting a pointer, which of them to use later in the
program.

Eg:

int f1(void);
char* f2(int);
/* ... many more of these to choose from */

/* ? correct declaration of generic_ptr ? */

if (/*...*/)
generic_ptr = f1;
else
generic_ptr = f2;

This is not possible, but you can do something like

void (*generic_ptr)();
if (/*...*/)
generic_ptr = (void(*)()) f1;
else
generic_ptr = (void(*)()) f2;

Although I usually consider typedefs for pointer types to be
a Bad Thing, this is one of the exceptions where it seems to
me the readability can be improved a lot:

typedef void (*FuncPtr)();
FuncPtr generic_ptr;
if (/*...*/)
generic_ptr = (FuncPtr)f1;
else
generic_ptr = (FuncPtr)f2;
generic_ptr(/* using correct args */)

You can *almost* do this, but only if all of your functions
obey some rather strange restrictions:

- All of f1, f2, ... and generic_ptr must return the same
type, or all must be void

AND

- None of f1, f2, ... and generic_ptr can be a variadic
function, or all must be variadic and have the same number
and types of fixed arguments

AND

- If the argument types are omitted (as above), then all the
arguments to all of f1, f2, ... must be non-promotable, or
all of f1, f2, ... must be defined with the obsolescent
"K&R" syntax.

The problem is that the type of the pointer with which you
call a function must agree with the actual type of the function
that is called. The strange restrictions listed above allow you
to skirt the agreement rule just a little bit -- but there's
still no way you can use the same pointer expression to call a
function returning int *and* a function returning double, for
example.

An alternative is to cast at the point of call, which again
is clarified by the use of some typedefs:

typedef int (*IntOfVoid)(void);
typedef char* (*StrOfInt)(int);
if (/*...*/)
i = ((IntOfVoid)generic_ptr)();
else
s = ((StrOfInt)generic_ptr)(42);
How should I define generic_ptr? Is (void*) ok? It seems to work on my
machine, but I would like to know if this is standard/portable.

See above. No. Happenstance, and it's not.

If the functions f1, f2, ... really bear no resemblance to
each other you're pretty much doomed to casting -- but in such a
case it's hard to see why you'd want to use a single "generic"
pointer in the first place. If they are related in some way but
differ in details, it may be better to use wrapper functions.
For example, suppose all the functions return int values, all
take two int arguments, and some also take a third string argument.
You could do something like

int f1(int, int, char*);
int f2(int, int, char*);
int f3(int, int); /* only two arguments */

int wrapf3(int x, int y, char* unused) {
return f3(x, y);
}

typedef int (*FuncPtr)(int, int, char*);
FuncPtr fptr;

if (/*...*/)
fptr = f1;
else if (/*...*/)
fptr = f2;
else
fptr = wrapf3; /* note indirection */

printf ("%d\n", fptr(42, 29, "Zaphod Rulez!"));

This technique may appear limiting at first glance, but when
you start applying it to actual "families" of "broadly similar"
functions it works quite well.
 
K

Keith Thompson

I am interested in having multiple functions with different prototypes
and deciding, by setting a pointer, which of them to use later in the
program.

Eg:

int f1(void);
char* f2(int);
/* ... many more of these to choose from */

/* ? correct declaration of generic_ptr ? */

if (/*...*/)
generic_ptr = f1;
else
generic_ptr = f2;

generic_ptr(/* using correct args */)

How should I define generic_ptr? Is (void*) ok? It seems to work on my
machine, but I would like to know if this is standard/portable.

void* is a generic object pointer type, in the sense that (1) you can
convert any object pointer to void* and back again, and get the
original pointer, and (2) such conversions can be done implicitly
(e.g., by a simple assignment rather than a cast).

There's no guarantee that converting a function pointer to or from
void* will give you anything meaningful. There is no generic function
pointer type in the sense of (2) above; conversions to and from
function pointer types must be explicit. But in the sense of (1),
*all* function pointer types are effectively generic; you can convert
a function pointer to any other function pointer type and back again
and get the same pointer. In effect, all function pointers "smell
alike", something that's not the case for object pointers.

But calling a function via a pointer of the wrong type invokes
undefined behavior.

Your best bet is probably to use something like ``void(*)(void)'',
(use a typedef) but you'll need to explicitly convert to or from this
type. Since there's no runtime representation of types (e.g., you
can't say ``if (typeof(x) == int)''), you'll need to use a switch
statement to select the proper type, and write each type of call
individually.
 
C

CBFalconer

Peter said:
.... snip ...

It isn't. The most common generic function pointer type is
void (*)(), though that won't cater for variadic functions.
Moreover, you generally need to cast to and from the original
function pointer in order to use it.

I believe the standard bans casts to and from function pointers.
 
W

Walter Roberson

I believe the standard bans casts to and from function pointers.

Casting between different function pointer types is explicitly
permitted; using the result is left undefined unless it matches
the actual call.

If I recall correctly, casting between a function pointer and an
object pointer (or the other way) is not banned, and that C
implementations are permitted to assign semantics to the result,
but the semantics are not specified in the C standards.

It -is- the case that the description of the semantics of
casting pointers takes care to group object pointers seperately from
function pointers. It is one of those clauses that is easy to
mis-read, in the sense that one's expectation (from experience)
that object pointers and function pointers "are" (as far as
one has ever encountered) convertable, can easily lead one to
overlook the seperation and "read in" a convertability that is
not present in the clause. If I had written the relevant paragraph,
I would probably have pointed out the non-covertability rather than
leaving it implied by simply leaving "object types" out of the list.
But then if I -had- written the standard, it probably would have been
four or five times as long as it turned out to be, as conciseness
never has been one of my strong points.
 
P

pete

Walter Roberson wrote:
If I recall correctly, casting between a function pointer and an
object pointer (or the other way) is not banned, and that C
implementations are permitted to assign semantics to the result,
but the semantics are not specified in the C standards.

That's what "undefined" means.

Implementations are permitted to assign semantics
to the result of (1 / 0) also.
 
K

Keith Thompson

pete said:
That's what "undefined" means.

Yes, but the previous poster had speculated that such casts are
"banned". They aren't.
Implementations are permitted to assign semantics
to the result of (1 / 0) also.

True.
 
J

jameskuyper

CBFalconer said:
I believe the standard bans casts to and from function pointers.

You believe incorrectly. Section 6.3.2.3p8:
" A pointer to a function of one type may be converted to a pointer to
a function of another
type and back again; the result shall compare equal to the original
pointer."
 
M

Malcolm McLean

I am interested in having multiple functions with different prototypes
and deciding, by setting a pointer, which of them to use later in the
program.

Eg:

int f1(void);
char* f2(int);
/* ... many more of these to choose from */
Don't go down this route.
Building an argument list at runtime is one of the very few operations that
C will not allow you to do.
You can cast to and from void (*fptr)(void) s and any other type on almost
every architecture, but that avails you little if you are then going to
hardcode the calls. You might as well do the job properly and use a union.
 
F

Flash Gordon

Malcolm McLean wrote, On 04/12/07 22:06:
Don't go down this route.
Building an argument list at runtime is one of the very few operations
that C will not allow you to do.
True.

You can cast to and from void (*fptr)(void) s and any other type on
almost every architecture,

You can do it on *every* architecture that has a C compiler because the
C standard says you can do it.
but that avails you little if you are then
going to hardcode the calls. You might as well do the job properly and
use a union.

Whether a union buys you anything will depend on the exact situation.
 

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

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top