473,700 Members | 2,884 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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 3651
"Beta What" <li**********@g mail.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_fun ction(char *) { ... }

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

Passing string_hash_fun ction to hash_insert will, in this case,
invoke undefined behavior, because it calls string_hash_fun ction
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************ **********@b68g 2000cwa.googleg roups.com>
Beta What <li**********@g mail.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_ite rator_string)(c onst char *);
typedef void (*hashtable_ite rator_double)(d ouble);
typedef void (*hashtable_ite rator_int_int)( int, int);
enum real_iterator_t ype { HT_IT_STRING, HT_IT_DOUBLE, HT_IT_INT_INT };

void ht_iterate(stru ct hashtable *ht, hashtable_itera tor_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_it erator_double)f unc)(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_it erator_int_int) func)(i1, i2);
break;
default:
panic("impossib le 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, "sufficient ly
flexible" prototype -- such as "void (*)(void *)". Now the
iterator itself looks like this:

typedef void (*hashtable_ite rator)(void *);

void ht_iterate(stru ct hashtable *ht, hashtable_itera tor 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(tabl e, (hashtable_iter ator_string)dou ble_iterator,
HT_IT_DOUBLE);

you have to write:

static void double_iterator (void *x0) {
double x = *(double *)x0;
sum += x;
}
...
ht_iterate(tabl e, 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(tabl e, 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(st ruct 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(ta ble, 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**********@g mail.com> wrote in message
news:11******** **************@ b68g2000cwa.goo glegroups.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
2335
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 <Windows.h>" at the start, and the following goes wrong : BOOL DPGetDefaultPrinter(LPTSTR pPrinterName, LPDWORD pdwBufferSize) { ....
4
1931
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 void (*vfptr)(void) ; typedef void (*vfptr_i)(int) ; typedef void (*vfptr_ii)(int, int ) ; typedef void (*vfptr_iis)(int, int, char*) ; the first declaration is used like a kind of base class functor in the
3
1528
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
1362
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 or smaller than sizeof(int). It seems to me there could be platforms where char = 8 bits, short = 16 bits, int = 32 bits,
28
16448
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 subset of C/C++, thus will have the same syntax as C/C++. Somehow the interpreted code must be able to store generic function pointers because there is no way for the interpreter to know every possible function signature in advance. I was...
0
8638
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9202
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8909
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
7791
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
6555
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5894
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4649
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
2371
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2018
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.