472,779 Members | 1,806 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,779 software developers and data experts.

Functions taking pointers to different types as arguments

Hello!

I'd like to have a variable of a pointer-to-function type. The two possible
values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!

-mi
Nov 14 '05 #1
9 4917
Mikhail Teterin wrote:
Hello!

I'd like to have a variable of a pointer-to-function type.
And what is the type of the function that you are referring to here.

For eg, you can use - PTR_TO_FUNC if you want a pointer
to a function with the prototype as that of fgetc as follows.

typedef int ( * PTR_TO_FUNC) ( FILE * );
PTR_TO_FUNC getter;
The two possible
values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!

-mi

Nov 14 '05 #2
In article <jZ********************@speakeasy.net>
Mikhail Teterin <us****@aldan.algebra.com> wrote:
I'd like to have a variable of a pointer-to-function type. The two possible
values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!


The problem is isomorphic to:

"What kind of pointer should I use to point to either a char
or a double, so that I can write *p = 3 to set the char to
control-C or the double to 3.0?"

The answer is: there is no such pointer. You must change the
problem.

In this case, the solution to the problem is to write a wrapper
function. Since we know "void *" is a general-purpose data
pointer, write a wrapper for fgetc():

int wrap_fgetc(void *vp) {
FILE *fp = vp;

return fgetc(fp);
}

Assuming gzgetc() has type "int gzgetc(void *)", you now have two
"int (void *)" functions, and can point to either one with:

int (*fp)(void *) = straight ? wrap_fgetc : gzgetc;

Of course, you must also convert the argument as well:

void *arg = straight ? the_fgetc_file : the_gzgetc_voidstar;

If your second function (gzgetc, here) had some other pointer
type as its sole parameter, you could wrap both functions, because
"void *" will always be able to hold any other pointer.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.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.
Nov 14 '05 #3
Mikhail Teterin <us****@aldan.algebra.com> wrote:

I'd like to have a variable of a pointer-to-function type. The two possible
values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!


There's no such type. What you're trying to do is not possible in
portable code. There's no guarantee that FILE * and void * are even the
same size let alone have the same representation, so you can't have a
single call that can invoke either one, you have to have separate calls
or write a wrapper function for one of the functions that has the same
type as the other function.

-Larry Jones

Let's pretend I already feel terrible about it, and that you
don't need to rub it in any more. -- Calvin
Nov 14 '05 #4
la************@ugs.com wrote:
Mikhail Teterin <us****@aldan.algebra.com> wrote:
I'd like to have a variable of a pointer-to-function type. The two
possible values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!

There's no such type. What you're trying to do is not possible in
portable code. There's no guarantee that FILE * and void * are even the
same size let alone have the same representation, so you can't have a
single call that can invoke either one, you have to have separate calls
or write a wrapper function for one of the functions that has the same
type as the other function.


The standard definition of qsort() seems to disagree with you here. The
callbacks passed to qsort are expected to take `const void *' arguments and
cast them internally.

Just as these callbacks know the true type of their arguments, both fgetc
and gzgetc in my example know, what's passed to them -- and my program
knows, which pointer to use.

As for pointers to different data-types being of different size, this is,
probably, even rarer than the (in)famous non-zero NULL pointer, is it not?
In my case, both functions (fgetc and gzgetc) expect a pointer to a
structure.

That said, a wrapper would, probably, be acceptable, as it will be optimized
away anyway :-)

Yours,

-mi
Nov 14 '05 #5
Chris Torek wrote:
The problem is isomorphic to:

"What kind of pointer should I use to point to either a char
or a double, so that I can write *p = 3 to set the char to
control-C or the double to 3.0?" The answer is: there is no such pointer. You must change the
problem.


No, it is not. Your analogy is flawed for two reasons:

1. Both of the different data pointers in my question are pointers to
structures (gzFile, known externally as void, and FILE), although
different structures. Such pointers are interchangable.

2. I'm not trying to assign any values to the memory being pointed
to by these either (no dereferencing) -- just passing them down to
functions (gzgetc, fgetc), which know how to deal with them.

There is also another pair of pointers in my example -- pointers to
functions (gzgetc and fgetc). This confusion may have led you to the flawed
analogy, but, unlike pointers to _different_ data-types, pointers to
functions are interchangable too.

-mi
Nov 14 '05 #6
Mikhail Teterin wrote:
la************@ugs.com wrote:

Mikhail Teterin <us****@aldan.algebra.com> wrote:
I'd like to have a variable of a pointer-to-function type. The two
possible values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!

There's no such type. What you're trying to do is not possible in
portable code. There's no guarantee that FILE * and void * are even the
same size let alone have the same representation, so you can't have a
single call that can invoke either one, you have to have separate calls
or write a wrapper function for one of the functions that has the same
type as the other function.

The standard definition of qsort() seems to disagree with you here. The
callbacks passed to qsort are expected to take `const void *' arguments and
cast them internally.


There is a difference between how qsort() works with its
comparison function and what you are attempting to do with a
kind of "one size fits all" function pointer.

When qsort() calls the comparison function, it passes two
`void*' arguments. The comparison function receives the `void*'
values and converts them to the desired pointer type. However,
qsort() does not pass an `int*' or a `struct foo*' or any such
thing, nor does the comparison function receive such a pointer:
qsort() passes a `void*' and the comparison function receives
a `void*', and that's that.
Just as these callbacks know the true type of their arguments, both fgetc
and gzgetc in my example know, what's passed to them -- and my program
knows, which pointer to use.
Function calls in C are "static" in the sense that the
number and types of all the arguments are known at compile
time and unchangeable thereafter. It is not possible to
write a call that sometimes passes a `void*' and sometimes
passes a `FILE*'.

The best you can do is to convert your `FILE*' to a `void*'
and pass it to a function that expects to receive a `void*'.
Your gzgetc() function can receive the `void*' directly, but
fgetc() cannot: it must receive a `FILE*', not a `void*' that
has been derived from the value of a `FILE*'. Hence Lawrence's
suggestion that you write a one-line wrapper that accepts the
`void*', converts it back to a `FILE*', and calls getc() with
the result:

int teterin_fgetc(void *fileptr) {
return getc((FILE*)fileptr);
}
As for pointers to different data-types being of different size, this is,
probably, even rarer than the (in)famous non-zero NULL pointer, is it not?
In English, this is called "begging the question." All
citizens are law-abiding, because lawbreakers are relatively
rare and shouldn't really be counted as citizens, right? All
crows are white, because any black crows I may happen to see
are probably ravens rather than true crows, okay? All pointers
are interchangeable, because I choose not to discuss systems
where they aren't; QED.

There are none so blind as those who will not see.
In my case, both functions (fgetc and gzgetc) expect a pointer to a
structure.
You described gzgetc() as taking a `void*', not a pointer
to a structure. Make up your mind ...

As for fgetc(), it most definitely does not expect or
accept a pointer to a structure; it requires a pointer to
a `FILE'. (No, don't post the contents of your system's
<stdio.h>; we're all aware of the sort of thing it probably
contains. Your <stdio.h> may well implement a `FILE*' as
a pointer to some kind of structure, but that doesn't imply
that every <stdio.h> does so. My system's <stdio.h> defines
_IOLBF as 64 and BUFSIZ as 1024; does that imply yours must
do the same?)
That said, a wrapper would, probably, be acceptable, as it will be optimized
away anyway :-)


Maybe, maybe not. Do you think the difference would be
measurable?

--
Er*********@sun.com

Nov 14 '05 #7
>Chris Torek wrote:
The problem is isomorphic to:
"What kind of pointer should I use to point to either a char
or a double, so that I can write *p = 3 to set the char to
control-C or the double to 3.0?"
The answer is: there is no such pointer. You must change the
problem.

In article <2659958.jYmr0OiCUC@misha>,
Mikhail Teterin <us****@aldan.algebra.com> wrote:No, it is not. Your analogy is flawed for two reasons:

1. Both of the different data pointers in my question are pointers to
structures (gzFile, known externally as void, and FILE), although
different structures. Such pointers are interchangable.
No, not according to the original posting anyway. You said that
one function took a "void *" pointer, the other a "FILE *".

Now, go find yourself a Data General Eclipse machine, and compile
the following source code:

#include <stdio.h>

int get1(void *vp) {
FILE *fp = vp;
return fgetc(vp);
}

Observe the assembler-level code (either disassemble the object code,
or compile to assembly). Note that the assignment "fp = vp" *uses
a shift instruction*. The assembly code turns out to be identical
to that for:

int get1(unsigned int vp) {
unsigned int fp = vp >> 1;
return fgetc(fp);
}

Now compare that to the assembly code for:

int get2(void *vp) {
return gzgetc(vp);
}

Here there is no shift instruction.

In other words, assignments from "FILE *" to "void *" require shifts
(left or right depending on which is the source and which is the
destination), while assignments from "void *" to "void *" are simply
bit-for-bit copies. You *must* get the shift instruction, via the
wrapper, for the version that calls fgetc(), because fgetc() expects
its argument to be pre-shifted.
2. I'm not trying to assign any values to the memory being pointed
to by these either (no dereferencing) -- just passing them down to
functions (gzgetc, fgetc), which know how to deal with them.
The gzgetc() function takes a "void *", and the fgetc() function
takes a "FILE *" (which is a "struct ... *"). These pointers have
different formats. The insides of both gzgetc() and fgetc() assign
values to the data structures to which the pointers point. These
assignments are hidden, but do occur.
There is also another pair of pointers in my example -- pointers to
functions (gzgetc and fgetc). This confusion may have led you to the flawed
analogy, but, unlike pointers to _different_ data-types, pointers to
functions are interchangable too.


Pointers to functions are *interconvertible*. This is not, at
least with my definition of "interchangeable", quite the same as
interchangeable. Consider int and double: we can take an int value,
convert it to a double, and (on most machines) preserve *all* the
original values (because int is typically 32 or fewer bits, while
double has at least 53 bits of mantissa). If we then convert the
resulting double back to int, we get the original value back --
but the conversion has changed things internally. The internal
representations of (int)3 and (double)3.0 are quite different (again,
on most machines[%] anyway).
-----
% The Burroughs A-series stored integers in floating-point format.
I have some doubts whether anyone could build a working C
implementation on one of these, even if any are still out there
to use, but if group did build a C compiler for one, they might
use the same representation for int and double.
-----

The same holds, in principle, for function pointers in C. The
standard guarantees that, given any two function types FT1 and FT2
and function f() of type FT1, the result of:

(FT1)(FT2)f

is just as useful as the original pointer. The "round trip
conversion" from FT1 to FT2, and then back to FT1, via the two
casts, preserves the value. The standard does *not* guarantee that
the internal representations are the same. Unlike the Eclipse
example, I do not know of any machines where the conversion really
does change the representation -- but it is *allowed*, so portable
code should be sure to cause both conversions. On the Eclipse,
when dealing with data pointers, both conversions are required;
code that achieves only one of them -- including incorrect qsort()
functions, which were one of the canonical examples -- fails at
runtime.

Most modern machines have only one internal representation for
pointers (the generic byte pointer), which has led people to forget
all the "interesting" problems that occurred when porting code to
the Eclipse, or even some of the funny "memory models" of the older
x86 (only some of which were actually standard-conforming). The
C standards (C89 and C99 both) do allow for them, though, and with
the advent of 64-bit CPUs, I suspect some of them will make a
comeback for a while.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.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.
Nov 14 '05 #8
Mikhail Teterin <us****@aldan.algebra.com> writes:
la************@ugs.com wrote:
Mikhail Teterin <us****@aldan.algebra.com> wrote:
I'd like to have a variable of a pointer-to-function type. The two
possible values are of type (*)(FILE *) and (*)(void *). For example:

getter = straight ? fgetc : gzgetc;
[...]
nextchar = getter(file);

What type should I give to `getter' so that the compiler does not issue
warnings about an incompatible value getting assigned to it? Thanks!

There's no such type. What you're trying to do is not possible in
portable code. There's no guarantee that FILE * and void * are even the
same size let alone have the same representation, so you can't have a
single call that can invoke either one, you have to have separate calls
or write a wrapper function for one of the functions that has the same
type as the other function.


The standard definition of qsort() seems to disagree with you here. The
callbacks passed to qsort are expected to take `const void *' arguments and
cast them internally.


Yes, but if you call qsort() with a comparison function that takes
pointers to structs, you'll invoke undefined behavior.

Different pointer types can be converted to each other (in some cases)
*if* the compiler has enough information to do the conversion, i.e.,
if the compiler knows both the source and target pointer types. If
you hide this information from the compiler by using a single pointer
type to refer to two distinct function types, the compiler will
complain. If you then use casts to force the compiler to accept the
mistyped pointer, you're lying to the compiler, and it will get its
revenge by giving you undefined behavior.

As it happens, you're likely to get away with it on most modern
systems, since many of them use only a single internal representation
for all pointer types. Don't think of this permissiveness as the
implementation being friendly; think of it as the implementation being
actively hostile by failing to diagnose errors that will show up at
the most inconvenient possible moment, several years from now, when
you try to port the code to a less "friendly" implementation.

There's no direct way to do what you're trying to do, but there are a
number of indirect ways to do it. You can store a pointer to either
the fgetc or gzgetc function in a single variable of some
pointer-to-function type, but you have to convert it back to the
proper type before performing a call through the function. That means
you have to know what the proper type is. Rather than using just a
pointer, use a struct consisting of a function pointer and a flag of
some sort that tells you the actual type of the function you're
pointing to. C doesn't let you store types directly, but you could
use an enumeration and choose what to do based on its value. You can
then do something like:

typedef void (*func_ptr)(void);
typedef int (*fgetc_ptr)(FILE*);
typedef int (*gzgetc_ptr)(void*);

if (straight) {
getter.func = (func_ptr)fgetc;
getter.kind = 'f';
}
else {
getter.func = (func_ptr)gzgetc;
getter.kind = 'g';
}

...

nextchar = (getter.kind == 'f' ? ((fgetc_ptr)getter.func)(file)
: ((gzgetc_ptr)getter.func)(file));

Wrap some of this in macros if you like.

Of course, as long as you're selecting the type based on the "kind",
you probably might as well select the function that way and avoid the
confusion of storing a pointer to it. The best approach depends on
the design of your application.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #9
Mikhail Teterin <us****@aldan.algebra.com> wrote:
Chris Torek wrote:
The problem is isomorphic to:

"What kind of pointer should I use to point to either a char
or a double, so that I can write *p = 3 to set the char to
control-C or the double to 3.0?"

The answer is: there is no such pointer. You must change the
problem.


No, it is not. Your analogy is flawed for two reasons:

1. Both of the different data pointers in my question are pointers to
structures (gzFile, known externally as void, and FILE), although
different structures. Such pointers are interchangable.


You do not know that a FILE is a struct. It is

# an object type capable of recording all the information needed to
# control a stream ...

but that does not necessarily make it a struct. It probably usually is
one, but it isn't demanded by the Standard.

Richard
Nov 14 '05 #10

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

Similar topics

99
by: David MacQuigg | last post by:
I'm not getting any feedback on the most important benefit in my proposed "Ideas for Python 3" thread - the unification of methods and functions. Perhaps it was buried among too many other less...
0
by: Anthony Baxter | last post by:
To go along with the 2.4a3 release, here's an updated version of the decorator PEP. It describes the state of decorators as they are in 2.4a3. PEP: 318 Title: Decorators for Functions and...
9
by: Jon Wilson | last post by:
I have a class which needs to accumulate data. The way we get this data is by calling a member function which returns float on a number of different objects of different type (they are all the...
2
by: Mackan | last post by:
Hi! I'm trying to declare a structure that will be including pointers to functions. These structures in turn will be pointed to in an array and depending on a index value that specific function...
6
by: Melkor Ainur | last post by:
Hello, I'm attempting to build an interpreter for a pascal-like language. Currently, I don't generate any assembly. Instead, I just build an abstract syntax tree representing what I've parsed...
14
by: Jess | last post by:
Hello, I learned that there are five kinds of static objects, namely 1. global objects 2. object defined in namespace scope 3. object declared static instead classes 4. objects declared...
5
by: Gene | last post by:
Hello all. Is the initializing assignment below ANSI standard- conforming? Is there a way to have the prefix parameters to ... type checked in such an assignment? E.g. in this case int x in the...
127
by: sanjay.vasudevan | last post by:
Why are the following declarations invalid in C? int f(); int f(); It would be great if anyone could also explain the design decision for such a language restricton. Regards, Sanjay
6
by: ma740988 | last post by:
I'm perusing a question and curiosity got the best of me in part because I cant solve the problem Consider: typedef unsigned int word_type ; class foo { public: void set_mx_digits_1 (...
0
by: erikbower65 | last post by:
Using CodiumAI's pr-agent is simple and powerful. Follow these steps: 1. Install CodiumAI CLI: Ensure Node.js is installed, then run 'npm install -g codiumai' in the terminal. 2. Connect to...
0
linyimin
by: linyimin | last post by:
Spring Startup Analyzer generates an interactive Spring application startup report that lets you understand what contributes to the application startup time and helps to optimize it. Support for...
0
by: erikbower65 | last post by:
Here's a concise step-by-step guide for manually installing IntelliJ IDEA: 1. Download: Visit the official JetBrains website and download the IntelliJ IDEA Community or Ultimate edition based on...
0
by: kcodez | last post by:
As a H5 game development enthusiast, I recently wrote a very interesting little game - Toy Claw ((http://claw.kjeek.com/))。Here I will summarize and share the development experience here, and hope it...
14
DJRhino1175
by: DJRhino1175 | last post by:
When I run this code I get an error, its Run-time error# 424 Object required...This is my first attempt at doing something like this. I test the entire code and it worked until I added this - If...
0
by: Rina0 | last post by:
I am looking for a Python code to find the longest common subsequence of two strings. I found this blog post that describes the length of longest common subsequence problem and provides a solution in...
5
by: DJRhino | last post by:
Private Sub CboDrawingID_BeforeUpdate(Cancel As Integer) If = 310029923 Or 310030138 Or 310030152 Or 310030346 Or 310030348 Or _ 310030356 Or 310030359 Or 310030362 Or...
0
by: lllomh | last post by:
How does React native implement an English player?
0
by: Mushico | last post by:
How to calculate date of retirement from date of birth

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.