Function pointers and default arguments

Discussion in 'C++' started by claudiu, Aug 4, 2007.

  1. claudiu

    claudiu Guest

    Hi,

    I'll go straight to the first question. Why does the code below
    compile?

    void f(int i = 0);

    int main()
    {
    (&f)();
    }

    My understanding is that the type of &f is void (*)(int) and as such,
    expects exactly one argument. However, the default argument of f is
    used.

    I tried to find out what the standard has to say but I couldn't find
    anything explicitly about it (I would appreciate if someone could
    point me to the correct paragraph).


    The second question, which is how I got to the previous one in the
    first place, is how can I "disable" the default arguments of a
    function. The reason for this is that I have to call a (large) number
    of functions with (lots of) default arguments based on values from an
    XML like structure. It's easy enough to read all the arguments from
    the XML and directly call the required function but I want the code to
    break if someone adds an extra default argument. This way people will
    be reminded to ammend the XML code to cope with the extra argument.

    The only easy solution I found so far is by using BOOST_TYPEOF.
    Something like:

    void FunctionWithLotsOfArgs(int, int = 0, double = 1.0, etc);

    // read params p1, p2,... from XmL
    BOOST_TYPEOF(&FunctionWithLotsOfArgs) fcn = &FunctionWithLotsOfArgs;
    fcn(p1, p2,...);

    However, there is a problem with this solution. It doesn't work on our
    current compiler. We are in the process of migrating to a new version
    but it may take a few months before this is completed.

    In my opinion, the obvious

    void (*FPtr)(int, int, ...) = &FunctionWithLotsOfArgs;
    FPtr(p1, p2, ...);

    is verbouse enough to put people off. But it works on our compiler.

    So, anyone knows any nifty tricks for disabling the default arguments?

    Regards,
    Claudiu

    P.S. Another solution I tried that fails on our compiler (it works on
    others) is

    template<typename T>
    class FuncPtr
    {
    public:
    FuncPtr(const T& ptr) : ptr_(ptr_){}
    const T& operator()() const { return ptr_; }
    private:
    T ptr_;
    };

    template<typename T>
    FuncPtr<T> MakeFuncPtr(const T& ptr)
    {
    return FuncPtr<T>(ptr);
    }

    void f(int i = 0);

    int main()
    {
    MakeFuncPtr(&f)()();
    }
     
    claudiu, Aug 4, 2007
    #1
    1. Advertising

  2. claudiu wrote:
    > Hi,
    >
    > I'll go straight to the first question. Why does the code below
    > compile?
    >
    > void f(int i = 0);
    >
    > int main()
    > {
    > (&f)();
    > }
    >
    > My understanding is that the type of &f is void (*)(int) and as such,
    > expects exactly one argument. However, the default argument of f is
    > used.


    Default parameters are part of the type.

    ....
    >
    > The second question, which is how I got to the previous one in the
    > first place, is how can I "disable" the default arguments of a
    > function. ...



    The code below does somthing like what you're looking at doing.
    -----
    // This F() takes function pointers of functions with no params
    template <typename Tr>
    Tr (* F( Tr (*fp)( ) ) )( )
    {
    return fp;
    }

    // This F(fp) takes function pointers of functions with 1 param
    template <typename Tr, typename T1>
    Tr (* F( Tr (*fp)( T1 ) ) )( T1 )
    {
    return fp;
    }

    // This F(fp) takes function pointers of functions with 2 params
    template <typename Tr, typename T1, typename T2>
    Tr (* F( Tr (*fp)( T1, T2 ) ) )( T1, T2 )
    {
    return fp;
    }

    // F(fp) may be extended to as many parameters you may want to handle


    // test code
    int Z( int i = 0)
    {
    return i;
    }

    int X( int i = 0, double d = 4.4)
    {
    return i;
    }

    int main()
    {
    Z();
    X();

    // works
    F( Z )(5);
    F( X )( 1, 1.1 );

    // fails
    //F( Z )();
    //F( X )( 1 );
    }
    -----

    This defines a number of overloaded F template functions that take
    pointers to functions of different numbers of parameters and return the
    pointer. The parameters do not define a "default" so the type of the
    functions returned do not have default parameters but are otherwise the
    same as the function passed in.
     
    Gianni Mariani, Aug 4, 2007
    #2
    1. Advertising

  3. claudiu

    James Kanze Guest

    On Aug 5, 12:23 am, Gianni Mariani <> wrote:
    > claudiu wrote:
    > > I'll go straight to the first question. Why does the code below
    > > compile?


    > > void f(int i = 0);


    > > int main()
    > > {
    > > (&f)();
    > > }


    > > My understanding is that the type of &f is void (*)(int) and as such,
    > > expects exactly one argument. However, the default argument of f is
    > > used.


    > Default parameters are part of the type.


    No they're not. The type of the function f, above, is void
    (int). Try it:
    void (*pf)( int ) = &f ;
    or even:
    void (*pf)( int i = 42 ) = &f ;

    The reason the default arguments are used in his case is because
    the expression invoking the function has default arguments
    associated with it.

    --
    James Kanze (GABI Software) email:james.kanze:gmail.com
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 4, 2007
    #3
  4. James Kanze wrote:
    > On Aug 5, 12:23 am, Gianni Mariani <> wrote:
    >> claudiu wrote:
    >>> I'll go straight to the first question. Why does the code below
    >>> compile?

    >
    >>> void f(int i = 0);

    >
    >>> int main()
    >>> {
    >>> (&f)();
    >>> }

    >
    >>> My understanding is that the type of &f is void (*)(int) and as such,
    >>> expects exactly one argument. However, the default argument of f is
    >>> used.

    >
    >> Default parameters are part of the type.

    >
    > No they're not. The type of the function f, above, is void
    > (int). Try it:
    > void (*pf)( int ) = &f ;
    > or even:
    > void (*pf)( int i = 42 ) = &f ;
    >
    > The reason the default arguments are used in his case is because
    > the expression invoking the function has default arguments
    > associated with it.


    Interesting; this code compiles in gcc but not on comeau or VC++ 2005.

    int f( int p = 5 )
    {
    return p;
    }

    template <typename T>
    T F( T fp )
    {
    return fp;
    }

    #include <iostream>

    int main()
    {
    std::cout << F( f )() << "\n";
    }

    Ok, so who is wrong, GCC or Comeau + VC++ ?
     
    Gianni Mariani, Aug 5, 2007
    #4
  5. claudiu

    Greg Herlihy Guest

    On Aug 4, 5:13 pm, Gianni Mariani <> wrote:
    > James Kanze wrote:
    > > On Aug 5, 12:23 am, Gianni Mariani <> wrote:
    > >> claudiu wrote:
    > >>> I'll go straight to the first question. Why does the code below
    > >>> compile?

    >
    > >>> void f(int i = 0);

    >
    > >>> int main()
    > >>> {
    > >>> (&f)();
    > >>> }

    >
    > >>> My understanding is that the type of &f is void (*)(int) and as such,
    > >>> expects exactly one argument. However, the default argument of f is
    > >>> used.

    >
    > >> Default parameters are part of the type.

    >
    > > No they're not. The type of the function f, above, is void
    > > (int). Try it:
    > > void (*pf)( int ) = &f ;
    > > or even:
    > > void (*pf)( int i = 42 ) = &f ;

    >
    > > The reason the default arguments are used in his case is because
    > > the expression invoking the function has default arguments
    > > associated with it.

    >
    > Interesting; this code compiles in gcc but not on comeau or VC++ 2005.
    >
    > int f( int p = 5 )
    > {
    > return p;
    >
    > }
    >
    > template <typename T>
    > T F( T fp )
    > {
    > return fp;
    >
    > }
    >
    > #include <iostream>
    >
    > int main()
    > {
    > std::cout << F( f )() << "\n";
    >
    > }
    >
    > Ok, so who is wrong, GCC or Comeau + VC++ ?


    gcc is wrong in this case - and acknowledges it:

    "The use of default arguments in function pointers, function typedefs
    and other places where they are not permitted by the standard is
    deprecated and will be removed from a future version of G++"

    from http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Deprecated-Features.html#Deprecated-Features

    Greg
     
    Greg Herlihy, Aug 5, 2007
    #5
  6. Greg Herlihy wrote:
    ....
    >> Ok, so who is wrong, GCC or Comeau + VC++ ?

    >
    > gcc is wrong in this case - and acknowledges it:
    >
    > "The use of default arguments in function pointers, function typedefs
    > and other places where they are not permitted by the standard is
    > deprecated and will be removed from a future version of G++"
    >
    > from http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Deprecated-Features.html#Deprecated-Features


    I think they only fixed half the bug.

    It appears that it only seems to work sometimes.

    int f1( int p = 5 )
    {
    return p;
    }

    int f2( int p )
    {
    return p;
    }

    double d1( double p )
    {
    return p;
    }

    double d2( double p = 1.1 )
    {
    return p;
    }

    template <typename T>
    T F( T fp )
    {
    return fp;
    }

    #include <iostream>

    int main()
    {

    std::cout << F( f1 )() << "\n";
    std::cout << F( f2 )() << "\n";

    std::cout << F( d1 )() << "\n";
    std::cout << F( d2 )() << "\n";
    }
     
    Gianni Mariani, Aug 5, 2007
    #6
  7. Gianni Mariani wrote:
    > Greg Herlihy wrote:
    > ...
    >>> Ok, so who is wrong, GCC or Comeau + VC++ ?

    >>
    >> gcc is wrong in this case - and acknowledges it:
    >>
    >> "The use of default arguments in function pointers, function typedefs
    >> and other places where they are not permitted by the standard is
    >> deprecated and will be removed from a future version of G++"
    >>
    >> from
    >> http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Deprecated-Features.html#Deprecated-Features
    >>

    >
    > I think they only fixed half the bug.


    Bug filed:
    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=32993
     
    Gianni Mariani, Aug 5, 2007
    #7
  8. claudiu

    claudiu Guest

    Gianni,

    Thanks for the solution! It seems to work with our compiler.

    > The reason the default arguments are used in his case is because
    > the expression invoking the function has default arguments
    > associated with it.
    >


    James,

    What do you mean by that last sentence?. The expression invoking the
    function is (&f)(). How are the default arguments associated with it?

    Regards,
    Claudiu
     
    claudiu, Aug 5, 2007
    #8
  9. claudiu

    James Kanze Guest

    On Aug 5, 2:13 am, Gianni Mariani <> wrote:
    > James Kanze wrote:


    [Note that as far as I can tell, this comment has nothing to
    do with what preceding in the thread.]
    > Interesting; this code compiles in gcc but not on comeau or VC++ 2005.


    > int f( int p = 5 )
    > {
    > return p;
    > }


    > template <typename T>
    > T F( T fp )
    > {
    > return fp;
    > }


    > #include <iostream>


    > int main()
    > {
    > std::cout << F( f )() << "\n";
    > }


    > Ok, so who is wrong, GCC or Comeau + VC++ ?


    What operator<< is being called? The type of F(f) is "int (*)(
    int )" (a "function" type is treated as a pointer to function
    type when it is a function parameter). There's no such
    operator<< in the standard, and there's no implicit conversion
    from a function pointer to anything which has an operator<<, so
    a priori, it shouldn't compile.

    What does the output look like? Could it be that g++ is
    (illegally) converting the function pointer to void*?

    --
    James Kanze (GABI Software) email:james.kanze:gmail.com
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 5, 2007
    #9
  10. claudiu

    James Kanze Guest

    On Aug 5, 1:59 pm, claudiu <> wrote:
    > Gianni,


    > Thanks for the solution! It seems to work with our compiler.


    > > The reason the default arguments are used in his case is because
    > > the expression invoking the function has default arguments
    > > associated with it.


    > What do you mean by that last sentence?. The expression invoking the
    > function is (&f)(). How are the default arguments associated with it?


    I'm not too sure of the exact rules myself, but the expression f
    designates a function for which a default argument is provided.
    While not part of the type, default arguments do propagate
    through expressions, remaining attached to the expression.

    Consider, for example:

    int f( int = 42 ) ;
    int g( int ) ;

    // ....
    assert( typeid( f ) == typeid( g ) ) ;

    But of course, f(), (&f)(), etc. remain legal.

    --
    James Kanze (GABI Software) email:james.kanze:gmail.com
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 5, 2007
    #10
  11. James Kanze wrote:
    > On Aug 5, 2:13 am, Gianni Mariani <> wrote:
    >> James Kanze wrote:

    >
    > [Note that as far as I can tell, this comment has nothing to
    > do with what preceding in the thread.]
    >> Interesting; this code compiles in gcc but not on comeau or VC++ 2005.

    >
    >> int f( int p = 5 )
    >> {
    >> return p;
    >> }

    >
    >> template <typename T>
    >> T F( T fp )
    >> {
    >> return fp;
    >> }

    >
    >> #include <iostream>

    >
    >> int main()
    >> {
    >> std::cout << F( f )() << "\n";
    >> }

    >
    >> Ok, so who is wrong, GCC or Comeau + VC++ ?

    >
    > What operator<< is being called?


    int ... I think.

    > ... The type of F(f) is "int (*)(
    > int )" (a "function" type is treated as a pointer to function
    > type when it is a function parameter). There's no such
    > operator<< in the standard, and there's no implicit conversion
    > from a function pointer to anything which has an operator<<, so
    > a priori, it shouldn't compile.
    >
    > What does the output look like?


    5

    > ... Could it be that g++ is
    > (illegally) converting the function pointer to void*?


    No. I don't think so.
     
    Gianni Mariani, Aug 5, 2007
    #11
  12. claudiu

    James Kanze Guest

    On Aug 5, 10:39 pm, Gianni Mariani <> wrote:
    > James Kanze wrote:
    > > On Aug 5, 2:13 am, Gianni Mariani <> wrote:
    > >> James Kanze wrote:


    > > [Note that as far as I can tell, this comment has nothing to
    > > do with what preceding in the thread.]
    > >> Interesting; this code compiles in gcc but not on comeau or VC++ 2005.


    > >> int f( int p = 5 )
    > >> {
    > >> return p;
    > >> }


    > >> template <typename T>
    > >> T F( T fp )
    > >> {
    > >> return fp;
    > >> }


    > >> #include <iostream>


    > >> int main()
    > >> {
    > >> std::cout << F( f )() << "\n";


    I missed the second pair of parentheses in the above.

    > >> }


    > >> Ok, so who is wrong, GCC or Comeau + VC++ ?


    > > What operator<< is being called?


    > int ... I think.


    > > ... The type of F(f) is "int (*)(
    > > int )" (a "function" type is treated as a pointer to function
    > > type when it is a function parameter). There's no such
    > > operator<< in the standard, and there's no implicit conversion
    > > from a function pointer to anything which has an operator<<, so
    > > a priori, it shouldn't compile.


    > > What does the output look like?


    > 5


    > > ... Could it be that g++ is
    > > (illegally) converting the function pointer to void*?


    > No. I don't think so.


    Doesn't really look like it, does it.

    It's actually an interesting case. While the default argument
    is not part of the type, it definitly is associated with the
    name somehow. It's not part of the type, since you can write
    something like:
    int f( int p = 5 ) ;
    int g( int p = 10 ) ;
    int (*pf)( int ) = condition ? f : g ;
    or, for that matter, if a header declares simply:
    int f( int ) ;
    you can add an:
    int f( int = 5 ) ;
    in one source file, and an:
    int f( int = 10 ) ;
    in another, and have different default arguments for the same
    function in different translation units---or even in different
    scopes in the same translation unit.

    Just out of curiousity, I modified your program to the
    following:
    #include <iostream>

    int
    f( int p = 42 )
    {
    return p ;
    }

    int g(
    int p = 5 )
    {
    return p ;
    }

    int count = 0 ;

    template< typename T >
    T F( T fp )
    {
    static bool instance = false ;
    if ( ! instance ) {
    ++ count ;
    instance = true ;
    }
    return fp ;
    }

    int
    main()
    {
    std::cout << F(f)() << std::endl ;
    std::cout << F(g)() << std::endl ;
    std::cout << count << std::endl ;
    return 0 ;
    }

    G++ (4.1.0) compiles it and outputs:
    42
    42
    1
    Which looks very much like a bug to me: it's (correctly)
    treating the two functions f and g as having the same type (and
    thus, only a single instantiation of the template), but
    associates the default argument for the first instantiation with
    all of the instantiations. (It would be interesting to see what
    happens when F is instantiated in several different translation
    units, with different default arguments.)

    With regards to the orignal problem: whether (&f)() should use
    the default argument or not, to be frank, I don't think that the
    standard is as clear as it could be about this. I'll raise the
    point in comp.std.c++, and see what they say there. (The
    declaration f has a default argument associated with it, but how
    far does this "annotation" propagate in an expression?)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 6, 2007
    #12
  13. Gianni Mariani, Aug 6, 2007
    #13
    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. Edward Diener
    Replies:
    14
    Views:
    5,079
    Josiah Carlson
    Apr 6, 2004
  2. tutmann
    Replies:
    4
    Views:
    453
  3. jmborr
    Replies:
    1
    Views:
    445
    Stargaming
    Nov 3, 2007
  4. Hamish
    Replies:
    3
    Views:
    599
    Alf P. Steinbach
    Jan 25, 2008
  5. cerr

    pointers, pointers, pointers...

    cerr, Apr 7, 2011, in forum: C Programming
    Replies:
    12
    Views:
    710
Loading...

Share This Page