*generic* pointer to function(s)?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • juerg.lemke@yahoo.com

    *generic* pointer to function(s)?

    Hi everyone

    I am interested in having multiple functions with different prototypes
    and deciding, by setting a pointer, which of them to use later in the
    program.

    Eg:

    int f1(void);
    char* f2(int);
    /* ... many more of these to choose from */

    /* ? correct declaration of generic_ptr ? */

    if (/*...*/)
    generic_ptr = f1;
    else
    generic_ptr = f2;

    generic_ptr(/* using correct args */)

    How should I define generic_ptr? Is (void*) ok? It seems to work on my
    machine, but I would like to know if this is standard/portable.

    Thank you for any help

    Juergen
  • Eric Sosman

    #2
    Re: *generic* pointer to function(s)?

    juerg.lemke@yah oo.com wrote:
    Hi everyone
    >
    I am interested in having multiple functions with different prototypes
    and deciding, by setting a pointer, which of them to use later in the
    program.
    >
    Eg:
    >
    int f1(void);
    char* f2(int);
    /* ... many more of these to choose from */
    >
    /* ? correct declaration of generic_ptr ? */
    >
    if (/*...*/)
    generic_ptr = f1;
    else
    generic_ptr = f2;
    This is not possible, but you can do something like

    void (*generic_ptr)( );
    if (/*...*/)
    generic_ptr = (void(*)()) f1;
    else
    generic_ptr = (void(*)()) f2;

    Although I usually consider typedefs for pointer types to be
    a Bad Thing, this is one of the exceptions where it seems to
    me the readability can be improved a lot:

    typedef void (*FuncPtr)();
    FuncPtr generic_ptr;
    if (/*...*/)
    generic_ptr = (FuncPtr)f1;
    else
    generic_ptr = (FuncPtr)f2;
    generic_ptr(/* using correct args */)
    You can *almost* do this, but only if all of your functions
    obey some rather strange restrictions:

    - All of f1, f2, ... and generic_ptr must return the same
    type, or all must be void

    AND

    - None of f1, f2, ... and generic_ptr can be a variadic
    function, or all must be variadic and have the same number
    and types of fixed arguments

    AND

    - If the argument types are omitted (as above), then all the
    arguments to all of f1, f2, ... must be non-promotable, or
    all of f1, f2, ... must be defined with the obsolescent
    "K&R" syntax.

    The problem is that the type of the pointer with which you
    call a function must agree with the actual type of the function
    that is called. The strange restrictions listed above allow you
    to skirt the agreement rule just a little bit -- but there's
    still no way you can use the same pointer expression to call a
    function returning int *and* a function returning double, for
    example.

    An alternative is to cast at the point of call, which again
    is clarified by the use of some typedefs:

    typedef int (*IntOfVoid)(vo id);
    typedef char* (*StrOfInt)(int );
    if (/*...*/)
    i = ((IntOfVoid)gen eric_ptr)();
    else
    s = ((StrOfInt)gene ric_ptr)(42);
    How should I define generic_ptr? Is (void*) ok? It seems to work on my
    machine, but I would like to know if this is standard/portable.
    See above. No. Happenstance, and it's not.

    If the functions f1, f2, ... really bear no resemblance to
    each other you're pretty much doomed to casting -- but in such a
    case it's hard to see why you'd want to use a single "generic"
    pointer in the first place. If they are related in some way but
    differ in details, it may be better to use wrapper functions.
    For example, suppose all the functions return int values, all
    take two int arguments, and some also take a third string argument.
    You could do something like

    int f1(int, int, char*);
    int f2(int, int, char*);
    int f3(int, int); /* only two arguments */

    int wrapf3(int x, int y, char* unused) {
    return f3(x, y);
    }

    typedef int (*FuncPtr)(int, int, char*);
    FuncPtr fptr;

    if (/*...*/)
    fptr = f1;
    else if (/*...*/)
    fptr = f2;
    else
    fptr = wrapf3; /* note indirection */

    printf ("%d\n", fptr(42, 29, "Zaphod Rulez!"));

    This technique may appear limiting at first glance, but when
    you start applying it to actual "families" of "broadly similar"
    functions it works quite well.

    --
    Eric.Sosman@sun .com

    Comment

    • juerg.lemke@yahoo.com

      #3
      Re: *generic* pointer to function(s)?

      Eric, Peter

      Thank you for the stupendously informative replies.

      J

      Comment

      • Keith Thompson

        #4
        Re: *generic* pointer to function(s)?

        juerg.lemke@yah oo.com writes:
        I am interested in having multiple functions with different prototypes
        and deciding, by setting a pointer, which of them to use later in the
        program.
        >
        Eg:
        >
        int f1(void);
        char* f2(int);
        /* ... many more of these to choose from */
        >
        /* ? correct declaration of generic_ptr ? */
        >
        if (/*...*/)
        generic_ptr = f1;
        else
        generic_ptr = f2;
        >
        generic_ptr(/* using correct args */)
        >
        How should I define generic_ptr? Is (void*) ok? It seems to work on my
        machine, but I would like to know if this is standard/portable.
        void* is a generic object pointer type, in the sense that (1) you can
        convert any object pointer to void* and back again, and get the
        original pointer, and (2) such conversions can be done implicitly
        (e.g., by a simple assignment rather than a cast).

        There's no guarantee that converting a function pointer to or from
        void* will give you anything meaningful. There is no generic function
        pointer type in the sense of (2) above; conversions to and from
        function pointer types must be explicit. But in the sense of (1),
        *all* function pointer types are effectively generic; you can convert
        a function pointer to any other function pointer type and back again
        and get the same pointer. In effect, all function pointers "smell
        alike", something that's not the case for object pointers.

        But calling a function via a pointer of the wrong type invokes
        undefined behavior.

        Your best bet is probably to use something like ``void(*)(void) '',
        (use a typedef) but you'll need to explicitly convert to or from this
        type. Since there's no runtime representation of types (e.g., you
        can't say ``if (typeof(x) == int)''), you'll need to use a switch
        statement to select the proper type, and write each type of call
        individually.

        --
        Keith Thompson (The_Other_Keit h) <kst-u@mib.org>
        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"

        Comment

        • pete

          #5
          Re: *generic* pointer to function(s)?

          Walter Roberson wrote:
          If I recall correctly, casting between a function pointer and an
          object pointer (or the other way) is not banned, and that C
          implementations are permitted to assign semantics to the result,
          but the semantics are not specified in the C standards.
          That's what "undefined" means.

          Implementations are permitted to assign semantics
          to the result of (1 / 0) also.

          --
          pete

          Comment

          • Keith Thompson

            #6
            Re: *generic* pointer to function(s)?

            pete <pfiland@mindsp ring.comwrites:
            Walter Roberson wrote:
            >If I recall correctly, casting between a function pointer and an
            >object pointer (or the other way) is not banned, and that C
            >implementation s are permitted to assign semantics to the result,
            >but the semantics are not specified in the C standards.
            >
            That's what "undefined" means.
            Yes, but the previous poster had speculated that such casts are
            "banned". They aren't.
            Implementations are permitted to assign semantics
            to the result of (1 / 0) also.
            True.

            --
            Keith Thompson (The_Other_Keit h) <kst-u@mib.org>
            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"

            Comment

            • Malcolm McLean

              #7
              Re: *generic* pointer to function(s)?

              <juerg.lemke@ya hoo.comwrote in message
              I am interested in having multiple functions with different prototypes
              and deciding, by setting a pointer, which of them to use later in the
              program.
              >
              Eg:
              >
              int f1(void);
              char* f2(int);
              /* ... many more of these to choose from */
              >
              Don't go down this route.
              Building an argument list at runtime is one of the very few operations that
              C will not allow you to do.
              You can cast to and from void (*fptr)(void) s and any other type on almost
              every architecture, but that avails you little if you are then going to
              hardcode the calls. You might as well do the job properly and use a union.

              --
              Free games and programming goodies.


              Comment

              • Flash Gordon

                #8
                Re: *generic* pointer to function(s)?

                Malcolm McLean wrote, On 04/12/07 22:06:
                <juerg.lemke@ya hoo.comwrote in message
                >I am interested in having multiple functions with different prototypes
                >and deciding, by setting a pointer, which of them to use later in the
                >program.
                >>
                >Eg:
                >>
                >int f1(void);
                >char* f2(int);
                >/* ... many more of these to choose from */
                >>
                Don't go down this route.
                Building an argument list at runtime is one of the very few operations
                that C will not allow you to do.
                True.
                You can cast to and from void (*fptr)(void) s and any other type on
                almost every architecture,
                You can do it on *every* architecture that has a C compiler because the
                C standard says you can do it.
                but that avails you little if you are then
                going to hardcode the calls. You might as well do the job properly and
                use a union.
                Whether a union buys you anything will depend on the exact situation.
                --
                Flash Gordon

                Comment

                Working...