typedef function with void parameters

C

Chris Dollin

William said:
This won't compile:

,----
| int foo(char *p)
| {}
|
| int main(int argc, char *argv[])
| {
| typedef int (*FUNC)(void *);
|
| FUNC f = foo;
| }
`----

The error is:

,----
| gcc -O2 /Users/william/studio/helloworlds/foo.cpp -lm -o foo
| /Users/william/studio/helloworlds/foo.cpp: In function 'int main(int, char**)':
| /Users/william/studio/helloworlds/foo.cpp:26: error: invalid conversion from 'int (*)(char*)' to 'int (*)(void*)'
`----

So, which do you want -- a function taking a `void*` or a function taking
a `char*`? They're not compatible types. Pick one.
 
W

William Xu

This won't compile:

,----
| int foo(char *p)
| {}
|
| int main(int argc, char *argv[])
| {
| typedef int (*FUNC)(void *);
|
| FUNC f = foo;
| }
`----

The error is:

,----
| gcc -O2 /Users/william/studio/helloworlds/foo.cpp -lm -o foo
| /Users/william/studio/helloworlds/foo.cpp: In function 'int main(int, char**)':
| /Users/william/studio/helloworlds/foo.cpp:26: error: invalid conversion from 'int (*)(char*)' to 'int (*)(void*)'
`----
 
W

William Xu

Chris Dollin said:
So, which do you want -- a function taking a `void*` or a function taking
a `char*`? They're not compatible types. Pick one.

Oh, i thought that most pointer types could be converted/stored into
void pointer. I'm trying to wrap different function pointers into FUNC,
like,

,----
| int foo(char *p){}
| int bar(int *p){}
|
| typedef int (*FUNC)(void *);
|
| FUNC f1 = foo, f2 = bar;
`----

So, this is not allowed?
 
M

Mark Bluemel

William said:
Oh, i thought that most pointer types could be converted/stored into
void pointer. I'm trying to wrap different function pointers into FUNC,
like,

,----
| int foo(char *p){}
| int bar(int *p){}
|
| typedef int (*FUNC)(void *);
|
| FUNC f1 = foo, f2 = bar;
`----

So, this is not allowed?

No - because the function types are different to the type of the
function variable.

You could perhaps force it :-

FUNC f1 = (FUNC)foo, f2 = (FUNC)bar;

But let's look at it from another angle....

If you did this and then called
f2("my dog has fleas");
what do you expect bar to receive?
 
T

tonnitielens

Converting a char* to a void* is different than converting a function
that takes a char* as a parameter to a function that takes a void* as
a parameter. You're mixing up things. ;)
 
B

Ben Bacarisse

William Xu said:
Oh, i thought that most pointer types could be converted/stored into
void pointer.

This is true of object pointers but not function pointers.
I'm trying to wrap different function pointers into FUNC,
like,

,----
| int foo(char *p){}
| int bar(int *p){}
|
| typedef int (*FUNC)(void *);
|
| FUNC f1 = foo, f2 = bar;
`----

So, this is not allowed?

Allowed is vague. It is a constraint violation and thus requires a
diagnostic (but that could be "I will convert these function pointers
for you" or it might be "Yuck! -- compilation halted").

If you convert (with a cast) it will work on some systems but it is
not guaranteed -- your code could break with a compiler update or even
a change in optimisation level. If you are prepared to take a chance,
always make sure that the call is though a pointer whose type matches
the type of function being called.
 
E

Eric Sosman

William said:
Oh, i thought that most pointer types could be converted/stored into
void pointer.

Yes, they can.
I'm trying to wrap different function pointers into FUNC,
like,

,----
| int foo(char *p){}
| int bar(int *p){}
|
| typedef int (*FUNC)(void *);
|
| FUNC f1 = foo, f2 = bar;
`----

So, this is not allowed?

No, it is not.

Here's why: You can convert a `void*' to or from any other
data pointer type, but you need to know what the other type is
in order to choose the proper conversion. Calling foo() or bar()
with a `void*' argument is all right, because the compiler knows
that it must convert `void*' to `char*' for foo() or convert
`void*' to `int*' for bar(). But when calling f1() -- more
precisely, when calling a function that f1 can point to --
you have told the compiler that no conversion is necessary:
an f1-addressable function takes a `void*' argument, so there
is no need to convert the `void*' that you supply. If f1
points (invalidly) at foo() or bar(), the compiler doesn't
know that it should perform a conversion.

On many machines these "conversions" are just bit-for-bit
copies of the pointers' values, but C does not require such a
simple addressing scheme and is able to run on machines whose
addressing is more intricate. So from the point of view of the
C language, you must keep the compiler properly informed about
the types of things.

Weak analogy: You know how to convert day-to-day natural
numbers to and from Roman numerals, and to and from English
utterances:

1234 <-> "MCCXXXIV"
1234 <-> "one thousand two hundred thirty-four"

Let's imagine that your bag of tricks includes C functions
to do these conversions, so you can think of them as "built-
in" like the conversions between `void*' and `double*'.

Now suppose you're writing a program that involves a few
more functions, each taking an argument representing a number
as a string. One of them wants a Roman representation, and
the other wants and English string:

void needsRoman(const char *roman);
void needsEnglish(const char *english);

When you want to call one of these with an argument that
is actually an integer, you need to do the proper conversion:

int x = 42;
needsRoman( intToRoman(x) );
needsEnglish( intToEnglish(x) );

What you have tried to do (the analogy approaches its
tortured conclusion; the air is thick with anticipation and
ennui) is to invent a construct that can pass `x' to either
needsRoman() or needsEnglish() without knowing which string
representation of `x' is appropriate. Can't be done: You
need to know what the called function expects to receive
before you can know how to meet its expectations.
 
J

jameskuyper

William said:
This won't compile:

,----
| int foo(char *p)
| {}

You've declared your function as returning an 'int', and then failed
to return anything. gcc will warn about these things; you should turn
the warning on. I normally use

gcc -ansi -pedantic -Wall -Wpointer-arith -Wcast-align -Wwrite-strings
-Wstrict-prototypes -Wmissing-prototypes
| int main(int argc, char *argv[])
| {
| typedef int (*FUNC)(void *);
|
| FUNC f = foo;

While it's no longer strictly necessary, I still think it's good
discipline to explicitly return a value from main(), too.
| }
`----

The error is:

,----
| gcc -O2 /Users/william/studio/helloworlds/foo.cpp -lm -o foo
| /Users/william/studio/helloworlds/foo.cpp: In function 'int main(int, char**)':
| /Users/william/studio/helloworlds/foo.cpp:26: error: invalid conversion from 'int (*)(char*)' to 'int (*)(void*)'
`----

It's perfectly legal to request that conversion, but it is not a
conversion that will occur implicitly. You have to perform an explicit
cast.

Note: the ONLY thing you can usefully do with the value of 'f' after
the conversion is convert it back to the original type, and then use
the restored function pointer to call the function. Here's a corrected
version:

int foo(char *p)
{ return 0; }

int main(int argc, char *argv[])
{
typedef int (*FUNC)(void *);

FUNC f = (FUNC)foo;

int (*g)(char*) = (int (*)(char*))f;
return g(argv[0]);
}
 
W

William Xu

You've declared your function as returning an 'int', and then failed
to return anything. gcc will warn about these things; you should turn
the warning on. I normally use

gcc -ansi -pedantic -Wall -Wpointer-arith -Wcast-align -Wwrite-strings
-Wstrict-prototypes -Wmissing-prototypes

[...]

While it's no longer strictly necessary, I still think it's good
discipline to explicitly return a value from main(), too.

Thanks. I'll be more careful before posting next time.
It's perfectly legal to request that conversion, but it is not a
conversion that will occur implicitly. You have to perform an explicit
cast.

Note: the ONLY thing you can usefully do with the value of 'f' after
the conversion is convert it back to the original type, and then use
the restored function pointer to call the function.

You are right. This seems an awkward usage, though.

I realize the better way is to learn from `qsort' function. Define a
common function pointer interface, implement(especially casting
correctly) it case by case.

I did an exercise:

---------------------------------8<-------------------------------------
#include <stdlib.h>
#include <stdio.h>

void print_any(void *any, void(*print)(void *p))
{
print(any);
}

void print_str(void *p)
{
printf("%s\n", (char*)p);
}

void print_int(void *p)
{
printf("%d\n", *(int*)p);
}

int main(int argc, char *argv[])
{
char *p = "hi";
int n = 2;

print_any(p, print_str);
print_any( & n, print_int);

return 0;
}
---------------------------------8<-------------------------------------
 
T

Tomás Ó hÉilidhe

William Xu said:
,----
| int foo(char *p)
| {}
|
| int main(int argc, char *argv[])
| {
| typedef int (*FUNC)(void *);
|
| FUNC f = foo;
| }


char* and void* are identical in how they work, i.e. their size and how
they store a given address as a bit pattern, but they're not the same
type as far as the compiler's concerned.

You can get your code to work with:

FUNC f = (int(*)(void*))foo;

and its behaviour will be well-defined, but you should probably rethink
your design. What are you gonna do when you come up against a pointer
type that's smaller than a void*? An int* for example?
 
O

Old Wolf

William Xu said:
| int foo(char *p)
| {}
|
| int main(int argc, char *argv[])
| {
| typedef int (*FUNC)(void *);
|
| FUNC f = foo;
| }

You can get your code to work with:

FUNC f = (int(*)(void*))foo;

and its behaviour will be well-defined

It wouldn't be well-defined to call the function
through f, however.
 
T

Tomás Ó hÉilidhe

Old Wolf said:
It wouldn't be well-defined to call the function
through f, however.


Given that char* and void* have identical representation, do you not think
it would be OK?

I'm not contesting what you said, I just want to see what people think
about it.

My thoughts presently are that I can't fathom why it could fail if char*
and void* are the same.
 
K

Keith Thompson

Tomás Ó hÉilidhe said:
Given that char* and void* have identical representation, do you not think
it would be OK?

I'm not contesting what you said, I just want to see what people think
about it.

My thoughts presently are that I can't fathom why it could fail if char*
and void* are the same.

Well, it can fail because the standard doesn't define the behavior of
such a call.

Realistically, I'd expect it to work under most, perhaps all,
real-world implementations. But I can easily imagine an
implementation that associates type information with function
pointers, and *deliberately* traps on such a call. Such an
implementation would likely impose some overhead on all indirect
function calls, but it would catch errors (defined, in this context,
as constructs whose behavior is not defined by the standard) than a
more traditional implementation would.
 
E

Eric Sosman

Tom�������������������������������� said:
Given that char* and void* have identical representation, do you not
think it would be OK?

There's a footnote somewhere in the Standard (I'm too lazy
to look it up at the moment) to the effect that the identical
representations are "intended" to allow interchangeability in
things like function calls. But a statement of intent is a
hint to the implementor, not a requirement that must be fulfilled;
footnotes are non-normative.
I'm not contesting what you said, I just want to see what people think
about it.

My thoughts presently are that I can't fathom why it could fail if char*
and void* are the same.

Nothing in the Standard forbids the use of different parameter-
passing mechanisms for `void*' and `char*', even though they share
the same representation. Maybe the machine has a repertoire of
string instructions that use "implied" registers, so the code is
optimized by passing a `char*' argument in one of these special-
purpose registers while using other general-purpose registers for
other arguments, including `void*'. If the caller passes its
`void*' argument in R1 and the callee tries to retrieve a `char*'
parameter from R0, "bit happens."

I don't know of any machines that use such a calling convention,
but there are a *lot* of machines I don't know, and there certainly
*are* machines whose string instructions use implied registers. The
idea may be odd, but it's not completely outlandish. The C Standard
goes to some lengths to permit implementations on peculiar machines,
recognizing that this is a fashion-driven industry and today's oddity
is tomorrow's must-have.
 
D

David Thompson

Allowed is vague. It is a constraint violation and thus requires a
diagnostic (but that could be "I will convert these function pointers
for you" or it might be "Yuck! -- compilation halted").
Right. Although I am a bit surprised that the OP's version of gcc
defaults the diagnostic to an error; mine make it a warning.
If you convert (with a cast) it will work on some systems but it is
not guaranteed -- your code could break with a compiler update or even
a change in optimisation level. If you are prepared to take a chance,
always make sure that the call is though a pointer whose type matches
the type of function being called.

_If_ you convert back to the correct (func pointer) type before
calling, it IS guaranteed to work. (Converting to and) calling through
the wrong pointer pointer is not guaranteed, and indeed could break,
although I'd be surprised if optimization does so -- C is usually and
traditionally implemented with separate compilation and linking, for
which optimization that doesn't eliminate a call entirely (inlining)
can usually change calling conventions only in very limited ways.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 

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

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top