Member function existence queries...

I

Ivan

How (at compile time) can one determine whether some class implements
a particular member function? Name only is sufficient, full signature
match nice but not required.

The application is a template class that (among other things) defines
an iterator class. The category of iterator depends on the template
argument type: if the argument defines only operator++ then the
iterator will be a forward_iterator, but if it also defines operator--
then the created iterator should be a bidirectional_iterator. That is,
something like this psudocode:

template<typename T> class Foo {
public:
typedef ("T defines --" ? bidirectional_iterator :
forward_iterator) Cat;
class iter : public iterator<..., ..., Cat, ...> {...}
};

The obvious approach is to specialize on the type of T. Unfortunately,
this code is in a general purpose library and the types that might be
the arguments are unknown and probably don't exist yet.

A second obvious approach is to require that the correct
iterator_category be passed in as an explicit argument. Unfortunately,
the requirement for the category is a long way from the library user
interface and asking the user to understand the need and correct value
for this extra argument would be a bit much.

A third approach would be to simply define the richest iterator
category and let it blow up if the actual type did not support
something the user tried to do. Unfortunately other code (STL for
example) will be specialized on the category of the iterators and so
will blow up if wrongly categorized even though it would work with the
correct categorization.

The Boost etc. constraint checking approaches don't work here: they
check for the presence of a concept, but give a compile error if it is
missing, whereas I accept missing without error but want to do
something different in that case.

I have tried to come up with a metaprogram that would do the necessary
test, and failed. The most promising approach was to create a class
that inherited from both the argument T and also from a dummy class
that does define operator-- but with a unique result type. Then a
"typeof(--TEST())" would be either whatever T's operator-- returned,
or the unique dummy type, and the two could be discriminated by
specialization. However, this approach fails because the compiler
throws an ambiguity error when both T and DUMMY have operator--. I
have not been able to create an inheritance tree that forces the
overloading inheritance rules to only pick T (if it has one) without
an ambiguity - ideas?

This cannot be a new problem - how do you do it?

Ivan
 
T

tom_usenet

How (at compile time) can one determine whether some class implements
a particular member function? Name only is sufficient, full signature
match nice but not required.

The application is a template class that (among other things) defines
an iterator class. The category of iterator depends on the template
argument type: if the argument defines only operator++ then the
iterator will be a forward_iterator, but if it also defines operator--
then the created iterator should be a bidirectional_iterator. That is,
something like this psudocode:

template<typename T> class Foo {
public:
typedef ("T defines --" ? bidirectional_iterator :
forward_iterator) Cat;
class iter : public iterator<..., ..., Cat, ...> {...}
};

I have tried to come up with a metaprogram that would do the necessary
test, and failed. The most promising approach was to create a class
that inherited from both the argument T and also from a dummy class
that does define operator-- but with a unique result type. Then a
"typeof(--TEST())" would be either whatever T's operator-- returned,
or the unique dummy type, and the two could be discriminated by
specialization. However, this approach fails because the compiler
throws an ambiguity error when both T and DUMMY have operator--. I
have not been able to create an inheritance tree that forces the
overloading inheritance rules to only pick T (if it has one) without
an ambiguity - ideas?

This cannot be a new problem - how do you do it?

This worked on Comeau - good luck making it work on anything else (it
should be possible on gcc with a few changes). I've not seen it
elsewhere (although it just uses SFINAE), but I can't believe it's the
first implementation...

#include <iterator>

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

template <bool b, typename T1, typename T2>
struct select
{
typedef T1 type;
};

template <typename T1, typename T2>
struct select<false, T1, T2>
{
typedef T2 type;
};

template <class T>
struct itcat
{
private:
template <class U, U&(U::*Ptr)() = &U::eek:perator-->
struct decrtester;

template <class U>
static no decrtest(...);

template <class U>
static yes decrtest(decrtester<U>*);

static bool const has_decr = sizeof(yes) ==
sizeof(decrtest<T>(0));

public:
typedef typename select<has_decr,
std::bidirectional_iterator_tag,
std::forward_iterator_tag>::type type;
};

struct It1
{
It1& operator--(){
return *this;
}
};

struct It2
{
};

#include <iostream>
#include <typeinfo>

int main()
{
std::cout << typeid(itcat<It1>::type).name() << '\n';
std::cout << typeid(itcat<It2>::type).name() << '\n';
}

Tom
 
F

Frank Schmitt

How (at compile time) can one determine whether some class implements
a particular member function? Name only is sufficient, full signature
match nice but not required.

The application is a template class that (among other things) defines
an iterator class. The category of iterator depends on the template
argument type: if the argument defines only operator++ then the
iterator will be a forward_iterator, but if it also defines operator--
then the created iterator should be a bidirectional_iterator. That is,
something like this psudocode:

template<typename T> class Foo {
public:
typedef ("T defines --" ? bidirectional_iterator :
forward_iterator) Cat;
class iter : public iterator<..., ..., Cat, ...> {...}
};

The following should do what you want:

<SNIP>

/** check at compile time, if function foo exists in given class.
* taken from postings in the boost mailing list
*/


#include <iostream>
//#include "boost/ref.hpp" // for BOOST_STATIC_CONSTANT
//#include "boost/static_assert.hpp" // for BOOST_STATIC_ASSERT

#define BOOST_STATIC_ASSERT( B ) \
enum { BOOST_JOIN(boost_static_assert_enum_, __LINE__) \
= sizeof:):boost::STATIC_ASSERTION_FAILURE< (bool)( B ) >) }

#define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment }


typedef char (&no_tag)[1];
typedef char (&yes_tag)[2];

template< typename T > no_tag has_member_foo_helper(...);

// template< typename T >
// yes_tag has_member_foo_helper(int, void (T::*)() = &T::foo);

template < typename T, void (T::*)() > struct ptmf_helper {};

template< typename T >
yes_tag has_member_foo_helper(int, ptmf_helper<T, &T::foo>* p = 0);

template< typename T >
struct has_member_foo
{
BOOST_STATIC_CONSTANT(bool
, value = sizeof(has_member_foo_helper<T>(0)) == sizeof(yes_tag)
);
};

struct my {};
struct her { void foo(); };


int main()
{
//BOOST_STATIC_ASSERT(!has_member_foo<my>::value);
//BOOST_STATIC_ASSERT(has_member_foo<her>::value);
std::cout << "has_member_foo(my): " << has_member_foo<my>::value << "\n";
std::cout << "has_member_foo(her): " << has_member_foo<her>::value << "\n";


return 0;
}

<SNAP>

But beware, some (many?) compilers won't compile this at all - I tried it
with MSVC 7.1, Comeau online, GCC 3.2.1 and Intel 7.0 - only MSVC and
Comeau compiled it.

HTH & kind regards
frank
 

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,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top