Function pointers and default arguments

C

claudiu

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)()();
}
 
G

Gianni Mariani

claudiu said:
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.
 
J

James Kanze

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

Gianni Mariani

James said:
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++ ?
 
G

Greg Herlihy

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
 
G

Gianni Mariani

Greg Herlihy wrote:
....
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";
}
 
C

claudiu

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
 
J

James Kanze

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

James Kanze

Thanks for the solution! It seems to work with our compiler.
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.
 
G

Gianni Mariani

James said:
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.
 
J

James Kanze

James said:
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.
int ... I think.
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?)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top