typedef function with void parameters

Discussion in 'C Programming' started by Chris Dollin, Dec 13, 2007.

  1. Chris Dollin

    Chris Dollin Guest

    William Xu wrote:

    > 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.

    --
    Chris "can't fit a quart into a magnetic pot" Dollin

    Hewlett-Packard Limited registered office: Cain Road, Bracknell,
    registered no: 690597 England Berks RG12 1HN
     
    Chris Dollin, Dec 13, 2007
    #1
    1. Advertising

  2. Chris Dollin

    William Xu Guest

    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*)'
    `----

    --
    William

    http://williamxu.net9.org
     
    William Xu, Dec 13, 2007
    #2
    1. Advertising

  3. Chris Dollin

    William Xu Guest

    Chris Dollin <> writes:

    > 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?

    --
    William

    http://williamxu.net9.org
     
    William Xu, Dec 13, 2007
    #3
  4. Chris Dollin

    Mark Bluemel Guest

    William Xu wrote:
    > Chris Dollin <> writes:
    >
    >> 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?


    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?
     
    Mark Bluemel, Dec 13, 2007
    #4
  5. Chris Dollin

    Guest

    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. ;)
     
    , Dec 13, 2007
    #5
  6. William Xu <> writes:

    > Chris Dollin <> writes:
    >
    >> 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.


    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.

    --
    Ben.
     
    Ben Bacarisse, Dec 13, 2007
    #6
  7. Chris Dollin

    Eric Sosman Guest

    William Xu wrote:
    > Chris Dollin <> writes:
    >
    >> 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.


    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.

    --
    Eric Sosman
    lid
     
    Eric Sosman, Dec 13, 2007
    #7
  8. Chris Dollin

    Guest

    William Xu wrote:
    > 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]);
    }
     
    , Dec 13, 2007
    #8
  9. Chris Dollin

    William Xu Guest

    writes:

    > 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<-------------------------------------

    --
    William

    http://williamxu.net9.org
     
    William Xu, Dec 13, 2007
    #9
  10. William Xu <> wrote in comp.lang.c:

    > ,----
    >| 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?

    --
    Tomás Ó hÉilidhe
     
    Tomás Ó hÉilidhe, Dec 13, 2007
    #10
  11. Chris Dollin

    Old Wolf Guest

    On Dec 14, 7:06 am, "Tomás Ó hÉilidhe" <> wrote:
    > William Xu <> wrote in comp.lang.c:
    >
    > >| 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.
     
    Old Wolf, Dec 13, 2007
    #11
  12. Old Wolf <> wrote in comp.lang.c:

    >> 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.



    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.

    --
    Tomás Ó hÉilidhe
     
    Tomás Ó hÉilidhe, Dec 14, 2007
    #12
  13. "Tomás Ó hÉilidhe" <> writes:
    > Old Wolf <> wrote in comp.lang.c:
    >>> 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.

    >
    > 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.

    --
    Keith Thompson (The_Other_Keith) <>
    Looking for software development work in the San Diego area.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Dec 14, 2007
    #13
  14. Chris Dollin

    Eric Sosman Guest

    "Tom��������������������������������" wrote:
    > Old Wolf <> wrote in comp.lang.c:
    >
    >>> 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.

    >
    >
    > 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.

    --
    Eric Sosman
    lid
     
    Eric Sosman, Dec 14, 2007
    #14
  15. On Thu, 13 Dec 2007 15:24:38 +0000, Ben Bacarisse
    <> wrote:

    > William Xu <> writes:


    > > | 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").
    >

    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
     
    David Thompson, Dec 24, 2007
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ollej Reemt
    Replies:
    7
    Views:
    552
    Jack Klein
    Apr 22, 2005
  2. Stig Brautaset

    `void **' revisited: void *pop(void **root)

    Stig Brautaset, Oct 25, 2003, in forum: C Programming
    Replies:
    15
    Views:
    795
    The Real OS/2 Guy
    Oct 28, 2003
  3. David M. Wilson
    Replies:
    8
    Views:
    492
    Ben Pfaff
    Jan 7, 2004
  4. Replies:
    5
    Views:
    843
    S.Tobias
    Jul 22, 2005
  5. Replies:
    1
    Views:
    413
    Victor Bazarov
    May 23, 2007
Loading...

Share This Page