Newsgroups: comp.lang.c
From: wkaufman@us.oracle.com (William Kaufman)
Subject: Re: Function pointer problem
Organization: Oracle Corporation, Redwood Shores CA
Date: Mon, 28 Sep 1992 20:29:32 GMT
X-Disclaimer: This message was written by an unauthenticated user
              at Oracle Corporation.  The opinions expressed are those
              of the user and not necessarily those of Oracle.

In article <1a5r9sINN6k1@almaak.usc.edu> ajayshah@almaak.usc.edu (Ajay Shah) writes:
] /* Consider this simple program.

    As an aside (not directed at Ajay), I always like posts that start,
"Consider the following,..."  It's so much classier than, "HELP ME!"

    ;-)

] typedef void *Observation;
] typedef void (obsprinter)(Observation);
] void somefunction(Observation x, obsprinter *f);
] 
] void floatpairprinter(float *x)
] {
]     printf("%f %f\n", *x, x[1]);
] }
] 
] int main()
] {
]     float *y;
]     somefunction((Observation) y, /* (obsprinter) */ floatpairprinter);
]     return 0;       
] }
] /* ------ end of program -----
] 
] This main program wants to think of a float * as being an Observation.

    But it's not.  Remember that (void *) and (float *) have nothing
whatsoever in common, aside from the fact that they both (presumably)
point into memory.  They may not be the same size, they might not be the
same "shape" (format).  You're lucky: on your machine (Sun, no doubt)
they *are* the same, which is why it works for you.

    Hauling out my hoary old DG example: On the Eclipse machines, there
are two pointer types: char/void pointers, and everything else.  Since,
in somefunction(), you're trying to pass off an Observation (void *) as
a (float *), your program will crash mercilessly on an Eclipse.

    The right way to do the above is to change the callee:

        void floatpairprinter(Observation x)
        {
            printf("%f %f\n", *(float *)x, ((float *)x)[1]);
        }

Or, declare a local (float *) version of x, cast/assign the argument
into the local copy, and use that one.  Now, only the cast on main()'s y
is needed.

] This is fine when casting y when handing it to somefunction.

    That's because there's a pointer there to cast.  You could cast
floatpairpointer to type (obsprinter), but that just means that
somefunction() has the wrong function pointer type, not that
floatpairprinter is magically correct.

    Here's the magic rule: You can cast a pointer argument to a function
to and from (void *) only as long as the callee never dereferences the
pointer without recasting it to its correct type.  That's it.

    Now, somefunction() probably doesn't dereference y, so this is all
fine and good.  It *does* dereference floatpairprinter (conceptually, if
not actually), though, when it makes the function call to it.
Therefore, you'd have to cast it back and cast somefunction()'s first
arg, too; this, of course, is ugly, and drives the compiler mad.

    Simply pay attention to the warnings and errors that lint or your
compiler (with full warnings) issue.  In many cases, you can't
understand them without understanding the compiler or the machines on
which that error causes a breakdown; so, just try to keep them happy and
you'll do OK.

                                           -- Bill K.

Bill Kaufman,          | "...all conscious species are plastic and
Corporate Lackey       |  all plastic species are conscious."
wkaufman@us.oracle.com |                     -- Yew-Kwang Ng