C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

compile-time discrimination of types

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Marco Jez
Guest





PostPosted: Wed Mar 16, 2005 9:03 am    Post subject: compile-time discrimination of types Reply with quote



Let's suppose I have a class template with parameter T. I want instances of
this template to be able to determine at compile time whether type T has a
function member which matches a certain signature or not, and then take
different actions in case such function exists. Something like this:

template<typename T>
struct Dummy
{
// should return true if T::method() exists, false otherwise
bool detect() const {....}
};

Is that possible?

Cheers,
Marco


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
sandor
Guest





PostPosted: Thu Mar 17, 2005 8:08 am    Post subject: Re: compile-time discrimination of types Reply with quote



Hi Marco,

I am attempting the same: checking for the existence of the clone
method with this code. But compilation fails in gcc 3.4.2. I was hoping
to use the "substitution failure is not an error" [SFINAE] rule, to
force the compiler to chose the second hasClone method if substituting
into the first one fails. But it is not working. Any help is
appreciated!


#include <iostream>
using namespace std;

template <size_t ignored>
struct IntSink
{
};

template <typename T>
struct CloneAsserter
{
enum { r = sizeof( &T::clone ) };
};

typedef char True;
struct False
{
char s[2];
};

template <typename C> True hasClone( IntSink< CloneAsserter *
);
template <typename C> False hasClone( ... );

template <typename T>
struct HasClone
{
enum { r = sizeof(hasClone<T>(0)) == sizeof(True) };
};

struct Tester1
{
int clone;
};

struct Tester2
{
int clone() { return 1; }
};

struct Tester3
{
};

#define PRINT(r) cout << #r " = " << r << endl;

int main()
{
PRINT( HasClone PRINT( HasClone<Tester2>::r );
PRINT( HasClone<Tester3>::r );
PRINT( HasClone<int>::r );
return 0;
}


cheers,
Sandor


Marco Jez wrote:
Quote:
Let's suppose I have a class template with parameter T. I want
instances of
this template to be able to determine at compile time whether type T
has a
function member which matches a certain signature or not, and then
take
different actions in case such function exists. Something like this:

template struct Dummy
{
// should return true if T::method() exists, false otherwise
bool detect() const {....}
};

Is that possible?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Victor Bazarov
Guest





PostPosted: Thu Mar 17, 2005 8:11 am    Post subject: Re: compile-time discrimination of types Reply with quote



Marco Jez wrote:
Quote:
Let's suppose I have a class template with parameter T. I want instances of
this template to be able to determine at compile time whether type T has a
function member which matches a certain signature or not, and then take
different actions in case such function exists. Something like this:

template struct Dummy
{
// should return true if T::method() exists, false otherwise
bool detect() const {....}
};

Is that possible?

This is just wrong. Call the function if you need to call it. The
compiler will issue an error if the T class doesn't have that member.
Branch if you have to, but use another T's function for that. Better
yet, specialise your template.

V

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
sandor
Guest





PostPosted: Thu Mar 17, 2005 8:15 am    Post subject: Re: compile-time discrimination of types Reply with quote

Hi,

I did solve it. Compiles on gcc 3.4.2.

--------------8<-----------------------------

#include using namespace std;

template <size_t ignored>
struct IntSink
{
};

template <typename T2, T2 p>
struct Assert
{
enum { r = sizeof( p ) };
};

typedef char True;
struct False
{
char s[2];
};

#define CHEKCER_CLASS_DEF(name)
template <typename T>
struct name
{
enum { r = sizeof(func##name<T>(0)) == sizeof(True) };
};


#define HAS_MEMBER_CHECKER(memberType, memberName, checkerName)
template <typename C> True func##checkerName( IntSink<
Assert * );
template <typename C> False func##checkerName( ... );
CHEKCER_CLASS_DEF(checkerName);

#define HAS_NONSTATIC_FIELD_CHECKER(fieldType, fieldName)
HAS_MEMBER_CHECKER(fieldType (C::*), fieldName,
Has_##fieldName##_nonstatic_field)

#define HAS_STATIC_FIELD_CHECKER(fieldType, fieldName)
HAS_MEMBER_CHECKER(fieldType *, fieldName,
Has_##fieldName##_static_field)

#define HAS_METHOD_CHECKER(retType, methodName, params)
HAS_MEMBER_CHECKER(retType(C::*)params, methodName,
Has_##methodName##_method)

#define HAS_SUBTYPE_CHECKER(SUBTYPE)
template <typename C> True funcHas_##SUBTYPE##_subtype( typename
C::SUBTYPE * );
template <typename C> False funcHas_##SUBTYPE##_subtype( ... );
CHEKCER_CLASS_DEF(Has_##SUBTYPE##_subtype);



HAS_NONSTATIC_FIELD_CHECKER(int, f)
HAS_STATIC_FIELD_CHECKER(int, f)
HAS_METHOD_CHECKER(C*, clone, ())
HAS_SUBTYPE_CHECKER(TestType)

struct Tester1
{
int f;
};

struct Tester2
{
Tester2 * clone() { return 0; }
};

struct Tester3
{
typedef int TestType;
static int f;
};

#define PRINT(r) cout << #r " = " << r << endl;

int main()
{
PRINT( Has_clone_method PRINT( Has_clone_method<Tester2>::r );
PRINT( Has_clone_method<Tester3>::r );
PRINT( Has_clone_method<int>::r );

PRINT( Has_f_nonstatic_field<Tester1>::r );
PRINT( Has_f_nonstatic_field<Tester2>::r );
PRINT( Has_f_nonstatic_field<Tester3>::r );
PRINT( Has_f_nonstatic_field<int>::r );

PRINT( Has_TestType_subtype<Tester1>::r );
PRINT( Has_TestType_subtype<Tester2>::r );
PRINT( Has_TestType_subtype<Tester3>::r );
PRINT( Has_TestType_subtype<int>::r );

PRINT( Has_f_static_field<Tester1>::r );
PRINT( Has_f_static_field<Tester2>::r );
PRINT( Has_f_static_field<Tester3>::r );
PRINT( Has_f_static_field<int>::r );


return 0;
}

--------------8<-----------------------------
It prints:

Has_clone_method Has_clone_method<Tester2>::r = 1
Has_clone_method<Tester3>::r = 0
Has_clone_method<int>::r = 0

Has_f_nonstatic_field<Tester1>::r = 1
Has_f_nonstatic_field<Tester2>::r = 0
Has_f_nonstatic_field<Tester3>::r = 0
Has_f_nonstatic_field<int>::r = 0

Has_TestType_subtype<Tester1>::r = 0
Has_TestType_subtype<Tester2>::r = 0
Has_TestType_subtype<Tester3>::r = 1
Has_TestType_subtype<int>::r = 0

Has_f_static_field<Tester1>::r = 0
Has_f_static_field<Tester2>::r = 0
Has_f_static_field<Tester3>::r = 1
Has_f_static_field<int>::r = 0


best regards,
Sandor




Marco Jez wrote:
Quote:
Let's suppose I have a class template with parameter T. I want
instances of
this template to be able to determine at compile time whether type T
has a
function member which matches a certain signature or not, and then
take
different actions in case such function exists. Something like this:

template struct Dummy
{
// should return true if T::method() exists, false otherwise
bool detect() const {....}
};

Is that possible?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Mike Jolley
Guest





PostPosted: Thu Mar 17, 2005 12:50 pm    Post subject: Re: compile-time discrimination of types Reply with quote

"Marco Jez" <guess (AT) none (DOT) it> wrote

Quote:
Let's suppose I have a class template with parameter T. I want instances
of
this template to be able to determine at compile time whether type T has a
function member which matches a certain signature or not, and then take
different actions in case such function exists. Something like this:

template struct Dummy
{
// should return true if T::method() exists, false otherwise
bool detect() const {....}
};

Is that possible?

This is similar to the following problem:

If a Clone method exists, use it. Otherwise use the copy constructor.

The solution usually involves the declaration of a type that represents
whether another type is Cloneable.

One thing you can do is to require that the member exists, so if it doesn't,
the program fails to compile. But it's trivial when defining templates to
make user code not compile. There are a few solutions that make a
function optional, avoiding the "doesn't compile" situation:

-- Use a template parameter that specifies how to do the function's job,
with a default of "do nothing". If the person using your template doesn't
know how to implement the function, nothing happens.

-- Use a traits class template. If this template is not specialized for T,
then the implementor of T isn't aware of your template and the default
action is used.

-- Have your template inherit a class that provides a trivial
implementation of the function and declare the function virtual. The
user can then derive from its instantiation of your template and
override the function if necessary.

There are disadvantages to all these things but it depends on what you
really need to do. There are ways of doing this I failed to mention,
some deliberately :)


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Marco Jez
Guest





PostPosted: Thu Mar 17, 2005 9:41 pm    Post subject: Re: compile-time discrimination of types Reply with quote

Quote:
I did solve it. Compiles on gcc 3.4.2.

Thanks Sandor, this is very close to what I'm trying to achieve. I have
further problems though, because the method I need to check existance for is
actually the operator==. Your approach works fine as long as this operator
is defined as member function of the struct/class, but of course it fails
when operator== is defined externally.

Any ideas?

To answer Victor and anyone who points at the conceptually wrong nature of
my question, I better explain what I'm trying to do. I'm writing a
reflection library and therefore I often need to mess with compile-time vs
run-time issues. I have a class named Value that acts as a container for
values of arbitrary types (similar to boost::any in concept, but different
in underlying semantics). Since this is a library I have no knowledge of the
user-defined types it will be used against, so template specialization is
not an option most of the times. Anyway, this Value class has a member
function named equal() that should test two Value objects for equality. The
goal is to provide actual equality test when the instances contained in the
Value objects support it through operator==, and to provide a run-time
fallback (for example a warning message) when they doesn't.

Marco


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Marco Jez
Guest





PostPosted: Thu Mar 17, 2005 9:47 pm    Post subject: Re: compile-time discrimination of types Reply with quote

Quote:
This is just wrong. Call the function if you need to call it. The
compiler will issue an error if the T class doesn't have that member.
Branch if you have to, but use another T's function for that. Better
yet, specialise your template.

The point is I don't want the compiler to issue an error, I want it to take
a different path. Exactly as Sandor said I was trying to exploit the SFINAE
rule to force the compiler choose a version of some type or function
template which is compatible with the method I'm trying to call (and to
choose another version if the member function doesn't exist). This works
fine if I want to check the existance of type definitions, but I need to
check the existance of functions instead.

Marco


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
sandor
Guest





PostPosted: Sat Mar 19, 2005 12:06 am    Post subject: Re: compile-time discrimination of types Reply with quote

Hi Marco,

I worked out the solution to the checking of the existence of the
global operator==, but unfortunately it crashes the gcc 3.4.2 compiler
itself: I get a segfault during the compilation. So this should be the
correct code to my best knowledge, but I am unable to test it. If I
change the names of the operator== methods to a simple "equals", the
code compiles and works.

#include <iostream>
using namespace std;

template <typename T2, T2 p>
struct Assert
{
};

typedef char True;
struct False
{
char s[2];
};

struct Tester1
{
int f;
};

struct Tester2
{
Tester2 * clone() { return 0; }
bool operator==(const Tester2 &) { return true; }
};

struct Tester3
{
typedef int TestType;
static int f;
};

bool operator==(const Tester3 &, const Tester3 &)
{ return true; }


template <typename T, bool (*equality)(const T &, const T &)>
struct EqualAssert
{
};

template <typename T> True funcHas_global_equality( EqualAssert<T,
operator==> * );
template <typename T> False funcHas_global_equality( ... );
template <typename T>
struct Has_global_equality
{
enum { r = sizeof(funcHas_global_equality<T>(0)) == sizeof(True) };
};


template <typename T> True funcHas_member_equality(
Assert<bool(T::*)(const T&), &T::operator==> * );
template <typename T> False funcHas_member_equality( ... );
template <typename T>
struct Has_member_equality
{
enum { r = sizeof(funcHas_member_equality<T>(0)) == sizeof(True) };
};

template <typename T>
struct Has_any_equality
{
enum { r = Has_global_equality<T>::r || Has_member_equality<T>::r };
};


#define PRINT(r) cout << #r " = " << r << endl;

int main()
{
PRINT( Has_global_equality PRINT( Has_global_equality<Tester2>::r );
PRINT( Has_global_equality<Tester3>::r );
PRINT( Has_global_equality<int>::r );

PRINT( Has_member_equality<Tester1>::r );
PRINT( Has_member_equality<Tester2>::r );
PRINT( Has_member_equality<Tester3>::r );
PRINT( Has_member_equality<int>::r );

PRINT( Has_any_equality<Tester1>::r );
PRINT( Has_any_equality<Tester2>::r );
PRINT( Has_any_equality<Tester3>::r );
PRINT( Has_any_equality<int>::r );

return 0;
}


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Vladimir Marko
Guest





PostPosted: Sat Mar 19, 2005 12:13 am    Post subject: Re: compile-time discrimination of types Reply with quote

Marco Jez wrote:
Quote:
I did solve it. Compiles on gcc 3.4.2.

Thanks Sandor, this is very close to what I'm trying to achieve. I
have
further problems though, because the method I need to check existance
for is
actually the operator==. Your approach works fine as long as this
operator
is defined as member function of the struct/class, but of course it
fails
when operator== is defined externally.
[snip]


So why don't you make two tests? One for member operator ==
and another one for namespace-scope operator ==. This shouldn't
be a problem _if you know the exact signatures_ (and the
namespace where you are looking for the operator). If you need
to find out if the type is "somehow" comparable (including
the use of conversions and argument dependent lookup) this
cannot currently be done. As I've writen recently in the thread
"detecting existence of a member function (SFINAE ?)" the main
problem is the open core issue #339. If there was a resolution
that allowed any expressions inside sizeof operator it would
be solved like

#include <cstddef>
struct no { };
struct yes { no no_[2]; };
template <std::size_t> struct yes_holder { typedef yes type; }

no tester(...);

// suppose that operator == does not return void Smile
template <typename T>
typename yes_holder<sizeof((*(T*)0)==(*(T*)0))>::type
tester(T*);

template <typename T>
struct has_equal{
static const bool value=
sizeof(yes)==sizeof(tester((T*)0));
};

I don't know a compiler that would correctly compile this
code today.

Regards,
Vladimir Marko


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Marco Jez
Guest





PostPosted: Sat Mar 19, 2005 10:40 am    Post subject: Re: compile-time discrimination of types Reply with quote

Quote:
So why don't you make two tests? One for member operator ==
and another one for namespace-scope operator ==.

That's what I tried, but the tester for namespace-scope operator doesn't
compile (it doesn't seem to follow the SFINAE rule, that is, it generates a
compile error if the operator is not defined).

Marco


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Marco Jez
Guest





PostPosted: Sat Mar 19, 2005 10:40 am    Post subject: Re: compile-time discrimination of types Reply with quote

Hi Sandor,

Quote:
I worked out the solution to the checking of the existence of the
global operator==, but unfortunately it crashes the gcc 3.4.2 compiler

it doesn't even compile here on MSVC++7.1, I get an error on this line:

template <typename T> True funcHas_global_equality( EqualAssert<T,
operator==> * );

the error message is:
"EqualAssert: template parameter 'equality' : 'operator`=='' : an expression
involving objects with internal linkage cannot be used as a non-type
argument"

Marco


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Vladimir Marko
Guest





PostPosted: Mon Mar 21, 2005 9:49 pm    Post subject: Re: compile-time discrimination of types Reply with quote

Marco Jez wrote:
Quote:
So why don't you make two tests? One for member operator ==
and another one for namespace-scope operator ==.

That's what I tried, but the tester for namespace-scope operator
doesn't
compile (it doesn't seem to follow the SFINAE rule, that is, it
generates a
compile error if the operator is not defined).

My mistake. I have to admit I didn't give it much thought.
My straightforward attempt looks like

struct no { };
struct yes { no no_[2]; };

template <typename T,T t>
struct yes_holder{ typedef yes type; };

no tester(...);

template <typename T>
typename yes_holder<bool (*)(const T&,const T&),&operator== >::type
tester(const T&);

template <typename T>
struct is_namespace_comparable{
static const bool value= sizeof(yes)==sizeof(tester(*(T*)0));
};

AFAICT the problem is that "&operator==" is non-dependent and
I see no possibility to make it dependent here. The natural way
of making it dependent is static_cast but the expression
"static_cast<bool(*)(const T&,const T&)>" does not qualify as
a non-type template argument. I already complained in some
older thread about the fact that static_cast<TP>(&X::foo)
is not a valid template argument even though &X::foo's type
is exactly TP and this is just another flavor of the same
problem.

[comments on other postings in the thread]
This also applies to the sandor's code. He wrote
<quote> If I change the names of the operator== methods to
a simple "equals", the code compiles and works.</quote>
but this is just a misleading corner case. It works thanks to
these special conditions: "equals" is defined prior the the
definition of template funcHas_global_equality; "equals" is
unique (try defining some more functions with this name and
gcc will emit an ICE). And the operator == brings some more
problems due to the comparison of built-in types (see the
MSVC 7.1 error you posted).

Detecting the namespace operator == seems to be impossible.
Unless someone finds a workaround, we have to wait for the
resolution of core DR339 to implement the is_comparable
metafunction.

Regards,
Vladimir Marko


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.