Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   type function U[1] and U(*)[1] SFINAE issue (http://www.velocityreviews.com/forums/t616565-type-function-u-1-and-u-1-sfinae-issue.html)

Fei Liu 05-23-2008 09:10 PM

type function U[1] and U(*)[1] SFINAE issue
 
Hello, I just hit a strange problem regarding SFINAE. The following code
causes compile error (void cannot be array element type), I thought
SFINA should match test(...) version instead and not issue any error.

If I replace U[1] with U(*)[1], then the code compiles again. I couldn't
make the sense out of it. What's the magic with (*)? Note that
function<int>::yes returns correct result 0 even with U[1].

Please help me out.

Fei

#include <iostream>
using namespace std;

template <typename T>
struct function{

template <typename U> static char test(...);
template <typename U> static char (&test(U[1]))[2];

enum { yes = (sizeof(function<T>::test<T>(0)) == 1) };
};

int main(){

cout << "int: " << function<int>::yes << endl;
cout << "void: " << function<void>::yes << endl;
// function
cout << "void(): " << function<void()>::yes << endl;
// function ptr
cout << "void(*)(): " << function<void(*)()>::yes << endl;
}

Barry 05-24-2008 02:16 AM

Re: type function U[1] and U(*)[1] SFINAE issue
 
On May 24, 5:10*am, Fei Liu <fei....@gmail.com> wrote:
> Hello, I just hit a strange problem regarding SFINAE. The following code
> causes compile error (void cannot be array element type), I thought
> SFINA should match test(...) version instead and not issue any error.
>
> If I replace U[1] with U(*)[1], then the code compiles again. I couldn't
> make the sense out of it. What's the magic with (*)? Note that
> function<int>::yes returns correct result 0 even with U[1].



What compiler are you using?

as long as U is a function type, U[1] is illformed, array of function
is not allowed.

when you say "U[1]" as a function parameter type, it's deprecated as
"U*"
when you say "U(*)[1]", it's a pointer to array of U.

>
> Please help me out.
>
> Fei
>
> #include <iostream>
> using namespace std;
>
> template <typename T>
> struct function{
>
> * * *template <typename U> static char test(...);
> * * *template <typename U> static char (&test(U[1]))[2];
>
> * * *enum { yes = (sizeof(function<T>::test<T>(0)) == 1) };
>
> };


could you explain your algorithm here?

>
> int main(){
>
> * * *cout << "int: " << * * * * * * *function<int>::yes << endl;
> * * *cout << "void: " << * * * * * * function<void>::yes << endl;
> * * *// function
> * * *cout << "void(): " << * * * * * function<void()>::yes << endl;
> * * *// function ptr
> * * *cout << "void(*)(): " << * * * *function<void(*)()>::yes << endl;
>
> }
>
>


a clean implementation of is_function from this NG a while ago:

by means of "function to pointer-to-function conversion"


typedef char (&yes) [1];
typedef char (&no) [2];

// is_class_type
template <typename T>
class is_class_type
{
template <typename S>
static yes check(int S::*);

template <typename S>
static no check(...);

public:
static bool const value = (sizeof(check<T>(0)) == sizeof(yes));
};

template <typename T>
class is_function
{
template <typename S>
static yes check(S*);

template <typename S>
static no check(...);

public:
static bool const value =
(!is_class_type<T>::value)
&&
(sizeof(check<T>(*(T*)(0))) == sizeof(yes));
};


__
Best Regards
Barry

courpron@gmail.com 05-24-2008 02:28 PM

Re: type function U[1] and U(*)[1] SFINAE issue
 
On 24 mai, 04:16, Barry <dhb2...@gmail.com> wrote:
> a clean implementation of is_function from this NG a while ago:
>
> by means of "function to pointer-to-function conversion"
>
> typedef char (&yes) [1];
> typedef char (&no) *[2];
>
> // is_class_type
> template <typename T>
> class is_class_type
> {
> * * template <typename S>
> * * static yes check(int S::*);
>
> * * template <typename S>
> * * static no check(...);
>
> public:
> * * static bool const value = (sizeof(check<T>(0)) == sizeof(yes));
>
> };
>
> template <typename T>
> class is_function
> {
> * * template <typename S>
> * * static yes check(S*);
>
> * * template <typename S>
> * * static no check(...);
>
> public:
> * * static bool const value =
> * * * * (!is_class_type<T>::value)
> * * * * &&
> * * * * (sizeof(check<T>(*(T*)(0))) == sizeof(yes));
>
> };


I think the following should do the same thing but in a shorter way :

template < class T >
struct is_function {
enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };
};

[------

Full working version without an "is_convertible" facility :

typedef char (&yes) [1];
typedef char (&no) [2];

template < class T >
struct is_function {
static yes test ( void(*)(T*) );
static no test(...);
enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;
};

------]

I didn't test this extensively though.

The rule I'm using from the Standard is :
8.3.5.3 : [...] The type of a function is determined using the
following rules. The type of each parameter is determined from its own
decl-specifier-seq and declarator. After determining the type of each
parameter, any parameter of type “array of T” or “function returning
T” is adjusted to be
“pointer to T” or “pointer to function returning T,” respectively.
[...]


Alexandre Courpron.

Barry 05-25-2008 02:44 PM

Re: type function U[1] and U(*)[1] SFINAE issue
 
On May 24, 10:28*pm, courp...@gmail.com wrote:
> On 24 mai, 04:16, Barry <dhb2...@gmail.com> wrote:
>
>
>
> > a clean implementation of is_function from this NG a while ago:

>
> > by means of "function to pointer-to-function conversion"

>
> > typedef char (&yes) [1];
> > typedef char (&no) *[2];

>
> > // is_class_type
> > template <typename T>
> > class is_class_type
> > {
> > * * template <typename S>
> > * * static yes check(int S::*);

>
> > * * template <typename S>
> > * * static no check(...);

>
> > public:
> > * * static bool const value = (sizeof(check<T>(0)) == sizeof(yes));

>
> > };

>
> > template <typename T>
> > class is_function
> > {
> > * * template <typename S>
> > * * static yes check(S*);

>
> > * * template <typename S>
> > * * static no check(...);

>
> > public:
> > * * static bool const value =
> > * * * * (!is_class_type<T>::value)
> > * * * * &&
> > * * * * (sizeof(check<T>(*(T*)(0))) == sizeof(yes));

>
> > };

>
> I think the following should do the same thing but in a shorter way :
>
> template < class T >
> struct is_function {
> * * enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };
>
> };
>
> [------
>
> Full working version without an "is_convertible" facility :
>
> typedef char (&yes) [1];
> typedef char (&no) *[2];
>
> template < class T >
> struct is_function {
> * * static yes test ( void(*)(T*) );
> * * static no test(...);
> * * enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;
>
> };
>
> ------]
>
> I didn't test this extensively though.
>
> The rule I'm using from the Standard is :
> 8.3.5.3 : [...] The type of a function is determined using the
> following rules. The type of each parameter is determined from its own
> decl-specifier-seq and declarator. After determining the type of each
> parameter, any parameter of type “array of T” or “function returning
> T” is adjusted to be
> “pointer to T” or “pointer to function returning T,” respectively.
> [...]


Very impressive!

Well, the implementation I mentioned and the one you did, both have a
defect:
T can't be reference type, as "pointer to reference is not allowed"

so a specialization of is_function is needed for reference type

template <typename T>
struct is_function<T&> {
enum { value = false };
};


Fei Liu 05-26-2008 06:33 PM

Re: type function U[1] and U(*)[1] SFINAE issue
 
On May 25, 10:44 am, Barry <dhb2...@gmail.com> wrote:
> On May 24, 10:28 pm, courp...@gmail.com wrote:
>
>
>
> > On 24 mai, 04:16, Barry <dhb2...@gmail.com> wrote:

>
> > > a clean implementation of is_function from this NG a while ago:

>
> > > by means of "function to pointer-to-function conversion"

>
> > > typedef char (&yes) [1];
> > > typedef char (&no) [2];

>
> > > // is_class_type
> > > template <typename T>
> > > class is_class_type
> > > {
> > > template <typename S>
> > > static yes check(int S::*);

>
> > > template <typename S>
> > > static no check(...);

>
> > > public:
> > > static bool const value = (sizeof(check<T>(0)) == sizeof(yes));

>
> > > };

>
> > > template <typename T>
> > > class is_function
> > > {
> > > template <typename S>
> > > static yes check(S*);

>
> > > template <typename S>
> > > static no check(...);

>
> > > public:
> > > static bool const value =
> > > (!is_class_type<T>::value)
> > > &&
> > > (sizeof(check<T>(*(T*)(0))) == sizeof(yes));

>
> > > };

>
> > I think the following should do the same thing but in a shorter way :

>
> > template < class T >
> > struct is_function {
> > enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };

>
> > };

>
> > [------

>
> > Full working version without an "is_convertible" facility :

>
> > typedef char (&yes) [1];
> > typedef char (&no) [2];

>
> > template < class T >
> > struct is_function {
> > static yes test ( void(*)(T*) );
> > static no test(...);
> > enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;

>
> > };

>
> > ------]

>
> > I didn't test this extensively though.

>
> > The rule I'm using from the Standard is :
> > 8.3.5.3 : [...] The type of a function is determined using the
> > following rules. The type of each parameter is determined from its own
> > decl-specifier-seq and declarator. After determining the type of each
> > parameter, any parameter of type “array of T” or “function returning
> > T” is adjusted to be
> > “pointer to T” or “pointer to function returning T,” respectively.
> > [...]

>
> Very impressive!
>
> Well, the implementation I mentioned and the one you did, both have a
> defect:
> T can't be reference type, as "pointer to reference is not allowed"
>
> so a specialization of is_function is needed for reference type
>
> template <typename T>
> struct is_function<T&> {
> enum { value = false };
>
> };


Here is the error message with comeau:
Comeau C/C++ 4.3.10.1 (May 7 2008 20:26:48) for
ONLINE_EVALUATION_BETA1
Copyright 1988-2008 Comeau Computing. All rights reserved.
BD Software STL Message Decryptor (Release 1.24 for Comeau C++)
MODE:strict errors C++ noC++0x_extensions

ComeauTest.c(8): error: array of void is not allowed
template <typename U> static char (&test(U[1]))[2];
^
detected during:
instantiation of "function<void>::test" based on template argument
<void>
at line 10
instantiation of class "function<void>" at line 16

ComeauTest.c(8): error: array of functions is not allowed
template <typename U> static char (&test(U[1]))[2];
^
detected during:
instantiation of "function<void ()>::test" based on template
argument <void
()> at line 10
instantiation of class "function<void ()>" at line 18

2 errors detected in the compilation of "ComeauTest.c".

I have no problem implementing the requirement. What I found strange
here is that SFINAE does not seem to work. Look at the error message,
it's an instantiation error, by definition, SFINAE should kick in and
the ... version should be used. That's what I don't get. I am not
quite sure if 8.3.5.3 is relevant here: array of T decays to pointer
to T. void * or void (*)() would have been ok. Why does not it cause
error with void (*)[1]? It's a pointer to an array of size 1 of void
type. Not fundamentally different from void[1], both are invalid
types.

Fei

Barry 05-29-2008 05:42 AM

Re: type function U[1] and U(*)[1] SFINAE issue
 
Fei Liu wrote:
> On May 25, 10:44 am, Barry <dhb2...@gmail.com> wrote:
>> On May 24, 10:28 pm, courp...@gmail.com wrote:
>>
>>
>>
>>> On 24 mai, 04:16, Barry <dhb2...@gmail.com> wrote:
>>>> a clean implementation of is_function from this NG a while ago:
>>>> by means of "function to pointer-to-function conversion"
>>>> typedef char (&yes) [1];
>>>> typedef char (&no) [2];
>>>> // is_class_type
>>>> template <typename T>
>>>> class is_class_type
>>>> {
>>>> template <typename S>
>>>> static yes check(int S::*);
>>>> template <typename S>
>>>> static no check(...);
>>>> public:
>>>> static bool const value = (sizeof(check<T>(0)) == sizeof(yes));
>>>> };
>>>> template <typename T>
>>>> class is_function
>>>> {
>>>> template <typename S>
>>>> static yes check(S*);
>>>> template <typename S>
>>>> static no check(...);
>>>> public:
>>>> static bool const value =
>>>> (!is_class_type<T>::value)
>>>> &&
>>>> (sizeof(check<T>(*(T*)(0))) == sizeof(yes));
>>>> };
>>> I think the following should do the same thing but in a shorter way :
>>> template < class T >
>>> struct is_function {
>>> enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };
>>> };
>>> [------
>>> Full working version without an "is_convertible" facility :
>>> typedef char (&yes) [1];
>>> typedef char (&no) [2];
>>> template < class T >
>>> struct is_function {
>>> static yes test ( void(*)(T*) );
>>> static no test(...);
>>> enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;
>>> };
>>> ------]
>>> I didn't test this extensively though.
>>> The rule I'm using from the Standard is :
>>> 8.3.5.3 : [...] The type of a function is determined using the
>>> following rules. The type of each parameter is determined from its own
>>> decl-specifier-seq and declarator. After determining the type of each
>>> parameter, any parameter of type “array of T” or “function returning
>>> T” is adjusted to be
>>> “pointer to T” or “pointer to function returning T,” respectively.
>>> [...]

>> Very impressive!
>>
>> Well, the implementation I mentioned and the one you did, both have a
>> defect:
>> T can't be reference type, as "pointer to reference is not allowed"
>>
>> so a specialization of is_function is needed for reference type
>>
>> template <typename T>
>> struct is_function<T&> {
>> enum { value = false };
>>
>> };

>
> Here is the error message with comeau:
> Comeau C/C++ 4.3.10.1 (May 7 2008 20:26:48) for
> ONLINE_EVALUATION_BETA1
> Copyright 1988-2008 Comeau Computing. All rights reserved.
> BD Software STL Message Decryptor (Release 1.24 for Comeau C++)
> MODE:strict errors C++ noC++0x_extensions
>
> ComeauTest.c(8): error: array of void is not allowed
> template <typename U> static char (&test(U[1]))[2];
> ^
> detected during:
> instantiation of "function<void>::test" based on template argument
> <void>
> at line 10
> instantiation of class "function<void>" at line 16
>
> ComeauTest.c(8): error: array of functions is not allowed
> template <typename U> static char (&test(U[1]))[2];
> ^
> detected during:
> instantiation of "function<void ()>::test" based on template
> argument <void
> ()> at line 10
> instantiation of class "function<void ()>" at line 18
>
> 2 errors detected in the compilation of "ComeauTest.c".
>
> I have no problem implementing the requirement. What I found strange
> here is that SFINAE does not seem to work. Look at the error message,
> it's an instantiation error, by definition, SFINAE should kick in and
> the ... version should be used. That's what I don't get. I am not
> quite sure if 8.3.5.3 is relevant here: array of T decays to pointer
> to T. void * or void (*)() would have been ok. Why does not it cause
> error with void (*)[1]? It's a pointer to an array of size 1 of void
> type. Not fundamentally different from void[1], both are invalid
> types.
>


I quite agree with you.
As the principle of SFINAE is to avoid producing invalid type, I
couldn't find why it doesn't work here


--
Best Regards
Barry


All times are GMT. The time now is 10:00 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.