Function versus pointer to function, in context of std::function,huh?

A

Alf P. Steinbach

When I do this:

typedef function< void( Type const& ) > DestroyFunc;

then Visual C++ 10.0 is happy.

But when I do this:

typedef function< void(*)( Type const& ) > DestroyFunc;

then it spews out a long sequence of silly error messages, like


<sillyErrorMessages>
1>c:\program files (x86)\microsoft visual studio
10.0\vc\include\functional(94): error C2027: use of undefined type
'std::tr1::_Get_function_impl<_Tx>'
1> with
1> [
1> _Tx=void (__cdecl *)(const int &)
1> ]
1> d:\dev\utility\clear_rounded_corners\cppx\owned.h(35) : see
reference to class template instantiation 'std::tr1::function<_Fty>'
being compiled
1> with
1> [
1> _Fty=void (__cdecl *)(const int &)
1> ]
1> d:\dev\utility\clear_rounded_corners\main.cpp(283) : see
reference to class template instantiation 'cppx::Owned<Type>' being compiled
1> with
1> [
1> Type=int
1> ]
1>c:\program files (x86)\microsoft visual studio
10.0\vc\include\functional(95): error C2504: '_Type' : base class undefined
1>c:\program files (x86)\microsoft visual studio
10.0\vc\include\functional(98): error C2027: use of undefined type
'std::tr1::_Get_function_impl<_Tx>'
1> with
1> [
1> _Tx=void (__cdecl *)(const int &)
1> ]
1>c:\program files (x86)\microsoft visual studio
10.0\vc\include\functional(98): error C2146: syntax error : missing ';'
before identifier '_Mybase'
1>c:\program files (x86)\microsoft visual studio
10.0\vc\include\functional(98): error C4430: missing type specifier -
int assumed. Note: C++ does not support default-int
1>c:\program files (x86)\microsoft visual studio
10.0\vc\include\functional(98): error C4430: missing type specifier -
int assumed. Note: C++ does not support default-int
</sillyErrorMessages>


What? Isn't that function type decaying to pointer to function type? And
if it isn't, shouldn't std::function be able to tackle pointer to
function type?


Cheers, & just wondering,

- Alf
 
N

Noah Roberts

When I do this:

typedef function< void( Type const& ) > DestroyFunc;

then Visual C++ 10.0 is happy.

But when I do this:

typedef function< void(*)( Type const& ) > DestroyFunc;

then it spews out a long sequence of silly error messages, like
....
What? Isn't that function type decaying to pointer to function type?
Nope.

And
if it isn't, shouldn't std::function be able to tackle pointer to
function type?

void whatever(Type const&);

function<void(Type const&)> fun = &whatever;
 
S

SG

When I do this:

   typedef function< void( Type const& ) > DestroyFunc;

then Visual C++ 10.0 is happy.

Right. The function template is declared/defined like this:

template<class Signature>
class function; // intentionally undefined

template<class Ret, class...Params>
class function<Ret(Params...)> {
...
};

So, in your case, Ret=void and Params...=Type const&
But when I do this:

   typedef function< void(*)( Type const& ) > DestroyFunc;

then it spews out a long sequence of silly error messages, like

Yes. This partial specialization from above does not apply and the
primary template is undefined. That's why you get the error. You
passed a T* instead of a R(P...) type with T = void(Type const&).
What? Isn't that function type decaying to pointer to function type?

There is no specialization for function pointer types. And types don't
decay. Just expressions do. The template parameter is supposed to be a
"signature" (return type and parameters).
And
if it isn't, shouldn't std::function be able to tackle pointer to
function type?

Not as its template parameter, no. Why should it?

Cheers!
SG
 
A

Alf P. Steinbach

Not as its template parameter, no. Why should it?

For usability.

Lots of function types are defined by libraries.

Usually, I dare say nearly always, as pointer to function.

With current limitations, instead of

function< WNDPROC >

I probably have to write something like

function< decltype( *(WNDPROC()) ) >

or whatever the syntax is for decltype, but anyway, why should the
programmer have to write that when the template could/should do it?


Cheers, & thanks,

- Alf
 
Q

Qi

I probably have to write something like

function< decltype( *(WNDPROC()) ) >

or whatever the syntax is for decltype, but anyway, why should the
programmer have to write that when the template could/should do it?

Or you can write some wrapper template

// not compiled code
template <typename T>
struct FunctionType
{
typedef boost::function<T> Result;
};

template <typename T>
struct FunctionType <T *>
{
typedef boost::function<T> Result;
};

Or don't use specialization, remove pointer in first template.

But why do you want to use boost::function?
I won't use a function object wrapper that can't use member
function directly. (bind? no...)
 
S

SG

[...]
I probably have to write something like

   function< decltype( *(WNDPROC()) ) >

or whatever the syntax is for decltype,

*(WNDPROC()) is not a name but an lvalue expression referring to a
function. As such, decltype is forced to yield an lvalue-reference-to-
function type which is also not viable as template parameter for
std::function said:
but anyway, why should the
programmer have to write that when the template could/should do it?

I don't see the value in this. You'd end up with distinct but
equivalent types with the exact same purpose. In your case:
function<void(*)()> and function<void()>. I would prefer transforming
the type before it's used as template parameter for function<>. But
that's just me.

SG
 
A

Alf P. Steinbach

[...]
I probably have to write something like

function< decltype( *(WNDPROC()) )>

or whatever the syntax is for decltype,

*(WNDPROC()) is not a name but an lvalue expression referring to a
function. As such, decltype is forced to yield an lvalue-reference-to-
function type which is also not viable as template parameter for
std::function<>.

That sounds rather impractical and at odds with the usual "reference
can't be detected".

I haven't checked but assume you're right.

I also assume that good old `typeof` (from e.g. g++) was more practical.

I'm just starting to use the C++0x stuff, it doesn't seem that
well-designed.

So, does one have to use some template stuff like
PointeeType<WndProc>::T, where a reader of code must look elsewhere for
definition, or can decltype be brought into service via some proper
black magic incantantion?

I don't see the value in this. You'd end up with distinct but
equivalent types with the exact same purpose. In your case:
function<void(*)()> and function<void()>.

You mention that as if it were some kind of problem?

What problem do you see with that?

Since we're dealing with such equivalence in practice anyway, e.g. decay
to pointer type everywhere a function type is used as formal argument
type, I really don't see any problem.

I would prefer transforming
the type before it's used as template parameter for function<>. But
that's just me.

No, it's probably not just you. There is a kind of fad of reversing the
principle of least redundancy. Of writing as many ungrokkable technical
details up front as possible, repeated each place instead of being
centralized somewhere, in order to make the code look less clear; a sort
of misguided and almost feminine attempt to *look* technical.


Cheers,

- Alf
 
N

Noah Roberts

Or you can write some wrapper template

// not compiled code
template <typename T>
struct FunctionType
{
typedef boost::function<T> Result;
};

template <typename T>
struct FunctionType <T *>
{
typedef boost::function<T> Result;
};

Or

template < typename T >
struct FunctionType<T*>
: FunctionType<typename std::remove_const<T>::type>
{};

template < typename T >
struct FunctionType<T&>
: FunctionType<typename std::remove_const<T>::type>
{};

I believe this would remove any pointer or reference appendages, no
matter how deep, and construct a function type out of whatever you pass in.
 
S

SG

That sounds rather impractical and at odds with the usual "reference
can't be detected".

It's more powerful than typeof.
I also assume that good old `typeof` (from e.g. g++) was more practical.

I agree that in many cases the behaviour of the typeof extension is
all you need. And in some cases it won't. (Think of how to implement
result_of<>). I expect to see many decltype uses similar to this:

template<class T, class U>
auto operator*(vec3<T> const& v, U s) -> vec3<decltype(p[0]*s)>;

where replacing decltype with typeof ought not to make a difference.
And if you really need the typeof behaviour very often, you may use
this shortcut:

#include <type_traits>
#define TYPEOF(xpr) typename std::decay<decltype(xpr)>::type

Since C++2011 allows the typename keyword to appear in more contexts
(including non-template ones) this macro should just work everywhere.
I'm just starting to use the C++0x stuff,
it doesn't seem that well-designed.

There are some little warts here and there, but all in all I'm very
happy with how things turned out.
So, does one have to use some template stuff like
PointeeType<WndProc>::T, where a reader of code must look elsewhere for
definition, or can decltype be brought into service via some proper
black magic incantantion?

I don't think so.
You mention that as if it were some kind of problem?
What problem do you see with that?

It's the principle of the matter. You end up with two different types
that could have been one. It complicates things. Will a
function<void(*)()> be convertible to a function<void()> and/or the
other way around? If not why not? Also, the types are not reference-
compatible.
Since we're dealing with such equivalence in practice anyway, e.g. decay
to pointer type everywhere a function type is used as formal argument
type, I really don't see any problem.

I really don't see any advantage. And I really don't like the let's-
just-use-two-distinct-types-for-the-same-thing strategy. :p

If you hate his so much you could write a proposal to give
std::function some other implementation-defined name (i.e.
std::_function_) and to create a type-transforming alias that emulates
this decay for types:

template<class T> struct aftt_ { typedef T type; };
template<class T> struct aftt_<T*> : aftt_<T> {};
template<class T> struct aftt_<T&> : aftt_<T> {};
template<class T> using function = _function_<typename
aftt_<T>::type>;

With this, function<void()> and function<void(*)()> would be
[...opinion...]

That's yours. I have mine.

Cheers!
SG
 
Q

Qi

The problem is, boost::function already supports two syntax.

Preferred syntax
boost::function<float (int x, int y)> f;

Portable syntax
boost::function2<float, int, int> f;

If it only supports first syntax, we can say it's how
it works and won't complain that it doesn't supports
function pointer syntax.

But since it already supports the two syntax, I can't see
why it doesn't supports the function pointer syntax, which
is more common accepted than function type, IMHO.

Of course it's the library provider's choice, so I won't
complain more (and I don't use boost::function). :)
 
A

Alf P. Steinbach

On 26 Jul., 17:49, Alf P. Steinbach wrote:
[about ability to write std::function said:
You mention that as if it were some kind of problem?
What problem do you see with that?

It's the principle of the matter. You end up with two different types
that could have been one. It complicates things.

The person with nick "Qi" notes else-sub-thread that boost::function
*already* has two different C++ types for each logical one, in order to
support TMP-challenged compilers.

And I noted that in dealing with functions in non-template contexts, we
already have two different C++ types for each logical one, e.g. void()
and void(*)().

And this is not problematic, and it's not complicated, to a C++
programmer. :)

Will a
function<void(*)()> be convertible to a function<void()> and/or the
other way around?

Of course, we're talking CALLABLE ENTITIES here.

If not why not? Also, the types are not reference-
compatible.

The problem of reference compatibility between int(double) and
int(*)(double) has never been a critical one.

It is simply not an issue of in-practice programming.

However, the ability to use the newfangled stuff (like std::function)
directly with the old stuff (like library typedefs of functions) IS a
practical issue. Programmers should not have to work around artificial
limitations of the standard library. That's absurd, but, we're there!

I really don't see any advantage. And I really don't like the let's-
just-use-two-distinct-types-for-the-same-thing strategy. :p

If you hate his so much you could write a proposal to give
std::function some other implementation-defined name (i.e.
std::_function_) and to create a type-transforming alias that emulates
this decay for types:

template<class T> struct aftt_ { typedef T type; };
template<class T> struct aftt_<T*> : aftt_<T> {};
template<class T> struct aftt_<T&> : aftt_<T> {};
template<class T> using function = _function_<typename
aftt_<T>::type>;

I really appreciate your attempt to help.

The above attempted solution is, however, both wrong and currently
impractical.

It's wrong, because it would let you use e.g. a pointer to pointer to
function type as template argument to std::function.

A pointer to pointer to function is something else entirely. There is no
convention going all the way back to 1974, or whatever year it was, of
using that to represent a function type, and no library you're using
will typedef such a type as a function type. So, accepting such types
would be supporting a never naturally occurring use case, and would
needlessly allow bugs to go unnoticed by turning off the type checking
for a number of forms (instead one might use std::remove_pointer).

Regarding that it's currently impractical, that's because Visual C++
10.0 does not yet support the template "using". Alas. I really like the
nifty type notation that it allows and that Johannes has been promoting
everywhere, defining "template< class T > using Type = T" he he.


Cheers,

- Alf
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top