472,373 Members | 1,903 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,373 software developers and data experts.

confusion: casting function pointers


Hello,

I have a question about casting a function pointer. Say I want to make
a generic module (say some ADT implementation) that requires a function
pointer from the 'actual/other modules' that takes arguments of type
(void *) because the ADT must be able to deal with any type of data.

In my actual code, I will code the function to take arguments of their
real types, then when I pass this pointer through an interface
function, I cast the function pointer (just changing the input argument
list to types of void * ).

My question is, when the ADT actually uses the function (inside the ADT
module, all the types are void *) will there be a cast to the 'rea'
types of the arguments (because my code defines the function to take
specific types, like char *) or will there be no cast (because the ADT
is using a function pointer that I had casted as one that takes void *
arguments)?

**** more specific question (example based) ***
What I really have in mind is something like a hash function that we
are sending to a Table ADT module. The hash function in our actual
code takes char * (string) input and returns an int. The Table ADT
wants a function pointer that takes a void * input and returns an int.
Say one defines the hashing function in their code to take a char * and
then casts it as a pointer to a function that takes void *. Then later
when the Table ADT code calls the hashpointer with what it thinks is a
void * (really a char *, but inside Table ADT code it's all void *),
will there be a cast to char *? This is important because the hash
funciton could be using pointer arithmetic on the input parameter.

I could test it on some compilers, but I believe some compilers allow
pointer arithmetic on void * types (as if they are char *).

Jul 1 '06 #1
3 3461
"Beta What" <li**********@gmail.com> writes:
What I really have in mind is something like a hash function that we
are sending to a Table ADT module. The hash function in our actual
code takes char * (string) input and returns an int. The Table ADT
wants a function pointer that takes a void * input and returns an int.
Say one defines the hashing function in their code to take a char * and
then casts it as a pointer to a function that takes void *. Then later
when the Table ADT code calls the hashpointer with what it thinks is a
void * (really a char *, but inside Table ADT code it's all void *),
will there be a cast to char *? This is important because the hash
funciton could be using pointer arithmetic on the input parameter.


I think you are essentially proposing this:

unsigned int string_hash_function(char *) { ... }

void hash_insert(unsigned int (*hash_function)(void *), void *p)
{
unsigned int hash_val = hash_function(p);
...
}

Passing string_hash_function to hash_insert will, in this case,
invoke undefined behavior, because it calls string_hash_function
through an incompatible function pointer.
--
"We put [the best] Assembler programmers in a little glass case in the hallway
near the Exit sign. The sign on the case says, `In case of optimization
problem, break glass.' Meanwhile, the problem solvers are busy doing their
work in languages most appropriate to the job at hand." --Richard Riehle
Jul 1 '06 #2
In article <11**********************@b68g2000cwa.googlegroups .com>
Beta What <li**********@gmail.com> wrote:
I have a question about casting a function pointer. Say I want to make
a generic module (say some ADT implementation) that requires a function
pointer from the 'actual/other modules' that takes arguments of type
(void *) because the ADT must be able to deal with any type of data.

In my actual code, I will code the function to take arguments of their
real types, then when I pass this pointer through an interface
function, I cast the function pointer (just changing the input argument
list to types of void * ).
Here is what I think you mean, translated into a concrete (if
a bit silly) example:

% cat dbl_module.c
/* this should really be in a .h file, but this is a short example */
extern void (*fptr)(void *, void *, void *);

static void dbl_sum(double *result, double *a, double *b) {
*result = *a + *b;
}

void dbl_init(void) {
fptr = (void (*)(void *, void *, void *))dbl_sum;
}
% cat main.c
#include <stdio.h>

void (*fptr)(void *, void *, void *);

int main(void) {
double x, y = 41.0, z = 1.0;

dbl_init();
(*fptr)(&x, &y, &z); /* line 9 */
printf("x = %f\n", x);
return 0;
}

These two files will compile, but there is no guarantee that the
result will actually work. While it is legal to set "fptr" as
shown in dbl_module.c, the actual call, at line 9 of main.c, passes
three "void *"s that are the result of converting three "double *"s.
The called function -- dbl_sum, in this case -- expects to receive
three "double *"s, not three "void *"s that have "double *" values
encoded somewhere inside them.

(The code will in fact work on most common machines today, but only
because "converting value from Pointer Of Type A to Pointer Of Type
B" is a no-op on those machines, primarily because they have only
one machine-level pointer type.)
My question is, when the ADT actually uses the function (inside the ADT
module, all the types are void *) will there be a cast to the 'rea'
types of the arguments (because my code defines the function to take
specific types, like char *) or will there be no cast (because the ADT
is using a function pointer that I had casted as one that takes void *
arguments)?
It sounds as though you think that a conversion is a cast. This
is not the case, in C at least. A cast is just the syntactic
construct consisting of a type-name enclosed in parentheses
followed by a value. That is:

double x;
int y = 0;

x = y; /* no cast here */
y = 3.1415926; /* no cast here */
y = (int)42; /* there is a cast here */

The first two assignments cause conversions, but there is no cast
involved. The third line causes a conversion from "int" to "int"
due to the cast. (This conversion just changes 42 to 42, i.e.,
has no real effect.)

In any actual function call, there are three items of interest:

- the prototype supplied at the point of the call, if any;
- the actual values supplied at the point of the call, and;
- the actual definition of the target function.

The last of these -- the actual definition of the function that
is being called -- is the sole determiner of what is "correct"
to pass to that function. If the target function is defined
without a prototype, a number of special "promotion" rules take
effect, and then the rest of the process is as if the target
function were defined with a prototype. So I think it is simplest
to assume that the target function will be defined with a
prototype (the promotion rules can be tacked on later; they
get a little complicated).

So, suppose we have the following as a target function:

T0 func(T1 param1, T2 param2, T3 param3) {
...
}

There are four types here: T0, the return type of the function;
and T1 through T3, the three types of the three parameters. There
are exactly three parameters. We can now state the restrictions
on the caller:

- If the caller has a prototype in scope at the point of the call,
that prototype must specify that the called function has
exactly three arguments, that its return type is T0, and
that the three arguments have type T1, T2, and T3 respectively.
The actual arguments will be converted to types T1, T2, and
T3 respectively as if by assignment. If any such assignment
would draw a diagnostic, the call must draw a diagnostic.

- If the caller lacks a prototype, the types T1 through T3 must
be unchanged under promotion, and the actual arguments must
already have the correct type (again after any promotion). In
any case the caller must have provided the type T0 correctly.

(If the target function uses ", ..." and <stdarg.h>, the actual
arguments that correspond to the ",..." part undergo the usual
promotion rules, as if there were no prototype. The sequence
of va_arg() macro invocations used to retrieve them must match
the actual-but-promoted arguments.)

This all brings up a last key point: how does the caller specify
the type of the called function? When calling a function "by name",
in the usual way:

result = func(arg1, arg2, arg3);

it is easy: the prototype comes from the prototype the caller
supplied for "func":

extern T0 func(T1, T2, T3);
...
result = func(arg1, arg2, arg3);

Here, if the prototype match the definition -- presumably it does
-- then the compiler can "vet" the call, converting the actual
arguments as if by assignment so that the converted results appear
in the appropriate formal parameters.

(If the caller forgot to provide a prototype, of course, the old
K&R-1 rules apply, with promotions. If the caller supplied an
incorrect prototype, the behavior is undefined.)

But what happens when the call is indirect, via a function pointer?
In this case, the prototype comes from the type of the function
pointer. So:

T0 (*fp)(T1, T2, T3) = func;
...
result = (*fp)(arg1, arg2, arg3);

This call is correct if and only if "*fp"'s type matches that of
"func" (or, equivalently, if fp's matches that of &func, and of
course "&func" is redundant in C, so there are a lot of ways to
put it).

What you seem to want to do is to store, in "fp", a pointer to
a function-pointer whose type is *not* "T0 (*)(T1, T2, T3)". The
C standard says that you may do this -- via a cast -- under one
condition: you must change the value *back* to the "correct"
type before the call. So, for instance, suppose we have another
function f2():

extern int f2(double);

We can then do this:

T0 (*fp)(T1, T2, T3);
int iresult;

fp = (T0 (*)(T1, T2, T3))f2; /* 1st cast */
iresult = (*(int (*)(double))fp)(42.0); /* 2nd cast */

Here, the prototype at the point of the call is supplied by the
second cast. Since "fp" holds the (converted) pointer to f2(),
and f2() really has type "int (*)(double)", we have to convert
the pointer back before the call.

Using this method, for callback functions in general (such as
for rewriting qsort()), is clumsy at the point of the call:
we have to decide which of some finite set of "actual function
types" the target function really has, and convert our pointer
to the right one. For instance:

typedef void (*hashtable_iterator_string)(const char *);
typedef void (*hashtable_iterator_double)(double);
typedef void (*hashtable_iterator_int_int)(int, int);
enum real_iterator_type { HT_IT_STRING, HT_IT_DOUBLE, HT_IT_INT_INT };

void ht_iterate(struct hashtable *ht, hashtable_iterator_string func,
enum realtype rt) {
const char *string_arg;
double double_arg;
int i1, i2;

for (...) { /* whatever it takes to iterate over entries */
switch (rt) {
case HT_IT_STRING:
/* figure out the string arg */
string_arg = entry->data;
(*func)(string_arg);
break;
case HT_IT_DOUBLE:
/* figure out the double arg */
double_arg = *(double *)entry->data;
(*(hashtable_iterator_double)func)(double_arg);
break;
case HT_IT_INT_INT:
/* figure out the two int args */
i1 = ((int *)entry->data)[0];
i2 = ((int *)entry->data)[1];
(*(hashtable_iterator_int_int)func)(i1, i2);
break;
default:
panic("impossible iterator type %d\n", (int)rt);
}
}
}

There is (usually) a better way. Instead of trying to enumerate
every possible call, simply require that every actual target function
be defined to match a *single*, pre-specified, "sufficiently
flexible" prototype -- such as "void (*)(void *)". Now the
iterator itself looks like this:

typedef void (*hashtable_iterator)(void *);

void ht_iterate(struct hashtable *ht, hashtable_iterator func) {
for (...) { /* whatever it takes to iterate over entries */
(*func)(entry->data);
}
}

Of course, this points the onus on the callee, instead of the
caller. Now, instead of writing:

static void double_iterator(double x) {
sum += x;
}
...
ht_iterate(table, (hashtable_iterator_string)double_iterator,
HT_IT_DOUBLE);

you have to write:

static void double_iterator(void *x0) {
double x = *(double *)x0;
sum += x;
}
...
ht_iterate(table, double_iterator);

But in fact, this is pretty straightforward; and if you distrust
casts (which is a sensible thing to do) you can now even write the
whole thing cast-free:

static void double_iterator(void *xp0) {
double *xp = x0;
sum += *xp;
}

Whenever you have control over the functions that will be called,
this is the method to use. If not, well...
**** more specific question (example based) ***
What I really have in mind is something like a hash function that we
are sending to a Table ADT module. The hash function in our actual
code takes char * (string) input and returns an int. The Table ADT
wants a function pointer that takes a void * input and returns an int.


In this case, you can "cheat". The C standards guarantee that
"char *" and "void *" have the same underlying representation, and
qualifiers (const, volatile, and in C99, "restrict") do not change
this. However, the type-safe thing to do -- the one that allows
you to avoid casts -- is to write a wrapper function.

Suppose, for instance, you have an "iterator" like the original
double_iterator shown above, but for some reason, you cannot alter
it to take a "void *". Suppose further that the function is
complicated enough not to simply rewrite it entirely. Then all
you have to do is write a wrapper:

static void double_iterator(double x) {
... something really complicated ...
}

static void wrapper(void *x0) {
double *xp = x0;
double_iterator(*xp);
}
...
ht_iterate(table, wrapper);

This version is once again clearly type-correct: the caller
(ht_iterate) believes it is calling a "void (*)(void *)", and your
wrapper is in fact a "void (*)(void *)". Your wrapper then obtains
the appropriate "double" and correctly passes it on to the "real"
target.

This method is also the key to writing fancy callback functions
that take multiple parameters. Instead of taking two, three, or
four separate parameters -- consider the HT_IT_INT_INT case above
and expanding it to "one int, one double, and one char *" -- you
simply wrap them all up into a structure, and pass its address as
converted to "void *":

/* stuff from some header file */
struct table; /* from the table module, perhaps */
extern void do_callbacks(struct table *, void (*)(void *), void *);
/* probably also in the table module */

/* stuff local to our own code */
struct args {
int ival;
double dval;
char *str;
/* more if needed */
};

static int callback(void *args0) {
struct args *args = args0;
int result;

/* note that you can use and/or modify any args->member here */

... work with args->ival, args->dval, and args->str ...

return result;
}

void somefunc(void) {
struct args args;
int final_result;

... set up "args" in advance if needed ...
final_result = do_callbacks(table, callback, &args);
... use any results stored in "args" if needed ...
}

The do_callbacks() code, over in some other module, has no idea --
and never *needs* to have any idea -- that callback() is actually
working with a large "struct" that collects up a bunch of data.

(In some other languages, this would be built-in and would be
called a "closure". :-) )
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Jul 1 '06 #3
"Beta What" <li**********@gmail.com> wrote in message
news:11**********************@b68g2000cwa.googlegr oups.com...
I have a question about casting a function pointer. Say I want to make
a generic module (say some ADT implementation) that requires a function
pointer from the 'actual/other modules' that takes arguments of type
(void *) because the ADT must be able to deal with any type of data.

In my actual code, I will code the function to take arguments of their
real types, then when I pass this pointer through an interface
function, I cast the function pointer (just changing the input argument
list to types of void * ).
A straightforward solution is to define the function with parameters of type
void * (or const void * if desired/appropriate), and convert the arguments
to the real types in the function. This does not require any casts at all,
which is usually a Good Thing.

[snip] **** more specific question (example based) ***
What I really have in mind is something like a hash function that we
are sending to a Table ADT module. The hash function in our actual
code takes char * (string) input and returns an int. The Table ADT
wants a function pointer that takes a void * input and returns an int.


Following my general advice above:

int hash_func(void *vp) {
/* convert argument to its real type: */
char *p = vp;

/* compute hash: */
int hash_value = 0;
/* ... apply hashing algorithm ... */
return hash_value;
}

Alternatively:

int real_hash_func(char *p);

int hash_func(void *vp) {
return real_hash_func(vp); /* void * to char * automatic due to
prototype */
}

See also <http://c-faq.com/lib/qsort2.html>.

Hope this helps,
Alex
Jul 1 '06 #4

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: Dirk Vanhaute | last post by:
I have only small knowledge of c++, but I would like to compile the example in http://support.microsoft.com/kb/q246772/ HOWTO: Retrieve and Set the Default Printer in Windows I included "#include...
4
by: Alfonso Morra | last post by:
I have come accross some code where function pointers are being cast from one type to another (admittedly, they all have the same return type). eg. you may have func ptrs declr as ff: typedef...
3
by: Alfonso Morra | last post by:
Hi, I have two func ptr prototypes declared as : typedef void (*VFP)(void) ; typedef int(MyCallback*)(enum_1, enum2, int, void*) ; I hav a struct as ff: typedef struct {
7
by: Nudge | last post by:
Hello everyone, Does the following code invoke undefined behavior? In other words, is it dangerous to lie to the compiler about a function's return type, when the return type is the same size...
28
by: Peter Olcott | last post by:
I want to make a generic interface between a scripting language and native code, the native code and the interpreter will both be written in C++. The interpreter will probably be implemented as a...
0
by: antdb | last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine In the overall architecture, a new "hyper-convergence" concept was proposed, which integrated multiple engines and...
0
hi
by: WisdomUfot | last post by:
It's an interesting question you've got about how Gmail hides the HTTP referrer when a link in an email is clicked. While I don't have the specific technical details, Gmail likely implements measures...
0
Oralloy
by: Oralloy | last post by:
Hello Folks, I am trying to hook up a CPU which I designed using SystemC to I/O pins on an FPGA. My problem (spelled failure) is with the synthesis of my design into a bitstream, not the C++...
0
by: Carina712 | last post by:
Setting background colors for Excel documents can help to improve the visual appeal of the document and make it easier to read and understand. Background colors can be used to highlight important...
0
by: Rahul1995seven | last post by:
Introduction: In the realm of programming languages, Python has emerged as a powerhouse. With its simplicity, versatility, and robustness, Python has gained popularity among beginners and experts...
2
by: Ricardo de Mila | last post by:
Dear people, good afternoon... I have a form in msAccess with lots of controls and a specific routine must be triggered if the mouse_down event happens in any control. Than I need to discover what...
1
by: Johno34 | last post by:
I have this click event on my form. It speaks to a Datasheet Subform Private Sub Command260_Click() Dim r As DAO.Recordset Set r = Form_frmABCD.Form.RecordsetClone r.MoveFirst Do If...
1
by: ezappsrUS | last post by:
Hi, I wonder if someone knows where I am going wrong below. I have a continuous form and two labels where only one would be visible depending on the checkbox being checked or not. Below is the...
0
DizelArs
by: DizelArs | last post by:
Hi all) Faced with a problem, element.click() event doesn't work in Safari browser. Tried various tricks like emulating touch event through a function: let clickEvent = new Event('click', {...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.