Function pointer oddity

I

Ian Collins

This one had me going today.

I have a class that distils down to

template <typename T>
struct X
{
mutable T t;
};

where T is the type of a function parameter extracted by a script. For
one function I kept getting bizarre compiler errors like these:

Member functions may not be declared with template type parameters.
A function cannot be declared to be mutable.

The hint came from g++:

field 'X<void ()()>::t' invalidly declared function type

It turned out one of the function's parameters was a callback typedef'd as:

typedef void (fn)()

Note the absent '*'. In the code it can be used interchangeably with
fn*, but when used as a template argument, fun ensues.

No wonder people call C's function pointer syntax crazy.
 
F

Francesco S. Carta

This one had me going today.

I have a class that distils down to

template <typename T>
struct X
{
mutable T t;
};

where T is the type of a function parameter extracted by a script. For
one function I kept getting bizarre compiler errors like these:

Member functions may not be declared with template type parameters.
A function cannot be declared to be mutable.

The hint came from g++:

field 'X<void ()()>::t' invalidly declared function type

It turned out one of the function's parameters was a callback typedef'd as:

typedef void (fn)()

Note the absent '*'. In the code it can be used interchangeably with
fn*, but when used as a template argument, fun ensues.

Good to know, that was new for me.

Just in case I'll have to stomp on it, how did you solve this issue?
 
I

Ian Collins

Good to know, that was new for me.

Just in case I'll have to stomp on it, how did you solve this issue?

I haven't as yet (but I asked that the typedef be changed!). It is a
tricky problem to solve. I originally thought of a wrapper along the
lines of

void f( fn ) {}
void f( fn* ) {}

But that won't work as the second appears as a redefinition of the first.
 
F

Francesco S. Carta

I haven't as yet (but I asked that the typedef be changed!). It is a
tricky problem to solve. I originally thought of a wrapper along the
lines of

void f( fn ) {}
void f( fn* ) {}

But that won't work as the second appears as a redefinition of the first.

I understand, let us know if you find a solution that does not involve
changing the original typedef - unfortunately I am not able to help, I
thought about creating a macro like:

#define fn *fn

to be inserted before the eventual header that declares that weird
typedef, but I'm quite sure it's a dummy attempt ;-)
 
I

Ian Collins

I understand, let us know if you find a solution that does not involve
changing the original typedef - unfortunately I am not able to help, I
thought about creating a macro like:

#define fn *fn

to be inserted before the eventual header that declares that weird
typedef, but I'm quite sure it's a dummy attempt ;-)

Actually the solution turns out to be a no-brainer.

My code generates a test (test::function()) version of the library
function and a wrapper to call it. As the offending template is in the
test function declaration, I just changed it to use a fn* and hand wrote
the wrapper.
 
F

Francesco S. Carta

Actually the solution turns out to be a no-brainer.

My code generates a test (test::function()) version of the library
function and a wrapper to call it. As the offending template is in the
test function declaration, I just changed it to use a fn* and hand wrote
the wrapper.

Sorry if I point out something dummy once more, but you always write
that as "fn*", shouldn't that be always "*fn"? (if not simply "fn()" in
the actual call)

Anyway, I'm not sure I understand how exactly you implemented it, could
you paste a snippet illustrating the final result?

That would ease my comprehension (as well other's, if somebody else
happens to have the same difficulty in working out the actual code).
 
I

Ian Collins

Sorry if I point out something dummy once more, but you always write
that as "fn*", shouldn't that be always "*fn"? (if not simply "fn()" in
the actual call)

Oops, I didn't see your reply. "fn*" is a pointer to type fn, "*fn" is
a function pointer type.
Anyway, I'm not sure I understand how exactly you implemented it, could
you paste a snippet illustrating the final result?

Well my code, which is used to generate mocks of library functions for
testing, generates a declaration for the mock function and wrapper
definition of the original function which calls the mock. Take sleep()
for example; the declaration is:

namespace test {
typedef OneParameterFunction<sleepId,unsigned,unsigned> sleep;
}

and the definition is:

int sleep(unsigned p0) { return test::sleep(p0); }

The declaration and definition are generated by macros (cough).

So all I had to do was use the declaration macro with the parameter type
of fn* and manually add the stub to match the true header declaration.
 
F

Francesco S. Carta

Oops, I didn't see your reply. "fn*" is a pointer to type fn, "*fn" is a
function pointer type.


Well my code, which is used to generate mocks of library functions for
testing, generates a declaration for the mock function and wrapper
definition of the original function which calls the mock. Take sleep()
for example; the declaration is:

namespace test {
typedef OneParameterFunction<sleepId,unsigned,unsigned> sleep;
}

and the definition is:

int sleep(unsigned p0) { return test::sleep(p0); }

The declaration and definition are generated by macros (cough).

So all I had to do was use the declaration macro with the parameter type
of fn* and manually add the stub to match the true header declaration.

Thank you for the further explanations.

By the way, I find nothing lamentable in using macros for generating
lots of patterned code, that kind of automation is also meant to save us
some good time in cases like this :)
 
J

James Kanze

This one had me going today.
I have a class that distils down to
template <typename T>
struct X
{
mutable T t;
};
where T is the type of a function parameter extracted by
a script. For one function I kept getting bizarre compiler
errors like these:
Member functions may not be declared with template type
parameters. A function cannot be declared to be mutable.
The hint came from g++:
field 'X<void ()()>::t' invalidly declared function type
It turned out one of the function's parameters was a callback
typedef'd as:
typedef void (fn)()
Note the absent '*'. In the code it can be used
interchangeably with fn*,

Only in a few specific cases. In this respect, function types
work a lot like array types: functions auto-convert to pointers
to functions in most contexts (even more contexts than arrays,
in fact), and a function parameter or return type declared as
a function is considered to be declared as a pointer to
function. (I seem to remember that return types are rewritten
as well, but I can't find the text in the standard at the
moment.) Note however that given the typedef above,
fn func;
is the equivalent of:
void func();
and not:
void (*func)();
And
void setCallback(fn& );
requires a function, and not a pointer to a function.

And as you noticed, when fn is used as a type argument to
a template, it is a function, and not a pointer.
but when used as a template argument, fun ensues.
No wonder people call C's function pointer syntax crazy.

No crazier than any of the rest of the declaration syntax. The
problem is the implicit conversions, and especially the rewrite
of function argument types. What's anomolous is that given the
above typedef:
void setCallback(fn callback);
works (and takes a pointer to a function).
 

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

Staff online

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top