Boost logo

Boost :

From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2007-07-26 18:54:24


On 7/26/07, Nikolay Mladenov <nikolay.mladenov_at_[hidden]> wrote:
> Hello,
>
> I am working on a patch for Boost.Python and
> I need to find a portable way of detecting if a static member function
> (with fixed name and signature) is declared in a class.
>
[snip]
>
> I have not tried other compilers but I expect more of the same problems.
>
> Can anyone suggest a better way of doing this?

I've been meaning to bring this up for awhile, so I'm glad you asked about this.

My experience with this sort of type introspection leads me to believe
that the devil is in the details. You're on the right track as far as
the set of compilers that are working (I wouldn't sweat 4.1.1, if
4.1.2 works), but you may want to test your code against all the
scenarios in MPL's has_xxx test suite. I found that while working out
the kinks with these SFINAE techniques, false positives were common
and even ICE could occur on different platforms.

However, I think I may have worked out those kinks well enough that my
effort could be reused, perhaps as the start of a compile time type
introspection library. A few months ago, I submitted a patch to MPL's
has_xxx utility to detect member templates. (The patch, svn ticket
#861, didn't make it through the transition from cvs. When I just
tried to upload it to Track it was rejected as spam.) Like I said,
getting the patch to work took some effort as well as help from folks
on this list. An e-mail describing the final product is archived at
http://tinyurl.com/3b3enm. You can copy and past the patch at the
bottom of the e-mail.

Anyway, others have suggested using SFINAE to build various compile
time type introspection capabilities for C++, and I kept that in mind
while I was working on detecting member templates. I think the
backbone is more or less in place for complete compile time type
introspection that could address your issue with static member
functions as well as non-static member functions, member data,
templates, types, etc. Rather than just HAS_XXX macros, there could be
HAS_MEMBER_TYPE, HAS_MEMBER_DATA, HAS_MEMBER_TEMPLATE,
HAS_MEMBER_FUNCTION, etc.

Basically, most of the work is infrastructure for the SFINAE
introspection/detection idiom and workarounds for compiler
deficiencies. For the most part, the only thing that changes between
the various sorts of members you can detect is the parameter of the
substitution template for triggering SFINAE and the syntax for
accessing the member.

As a proof of concept that may or may not also assist your immediate
need in working on Boost.Python, the example below uses the
preprocessor metafunctions from my patch to illustrate how
HAS_MEMBER_TEMPLATE, HAS_MEMBER_FUNCTION, and
HAS_MEMBER_STATIC_FUNCTION can be implemented. First apply the patch
at the link above, and then see comments in boost/mpl/has_xxx.hpp for
more info. The example is not intended to be robust in every use case
and compiler, but should illustrate the concept. See has_xxx.hpp to
get a sense of how the example would be integrated with the existing
compiler workarounds for broad coverage.

If there's interest in these introspection tools either as part of
MPL, or maybe type_traits, or even a new Boost.Introspection library,
I can productize/boostify this and put code on vault. I may not be
able to get to it right away, but I'd be glad to write test cases and
documentation.

Hope this helps.
Daniel

#include <boost/mpl/assert.hpp>
#include <boost/mpl/has_xxx.hpp>

// Introspection for member templates.
#define HAS_MEMBER_TEMPLATE_ACCESS( \
            args, class_type, param \
        ) \
    class_type::template BOOST_PP_ARRAY_ELEM(1, args) \
/**/

#define HAS_MEMBER_TEMPLATE_SUBSTITUTE_PARAMETER( \
            args, param \
        ) \
    template< \
        BOOST_PP_ENUM_PARAMS( \
            BOOST_PP_ARRAY_ELEM(2, args) \
          , typename param \
        ) \
> \
    class param\
/**/

#define HAS_MEMBER_TEMPLATE(name, n) \
    BOOST_MPL_HAS_MEMBER_IMPLEMENTATION( \
        ( 4, ( BOOST_PP_CAT(has_, name), name, n, false ) ) \
      , BOOST_MPL_HAS_MEMBER_INTROSPECT \
      , HAS_MEMBER_TEMPLATE_SUBSTITUTE_PARAMETER \
      , HAS_MEMBER_TEMPLATE_ACCESS \
    ) \
/**/

// Introspection for member functions.
#define HAS_MEMBER_FUNCTION_ACCESS( \
            args, class_type, param \
        ) \
    &class_type::BOOST_PP_ARRAY_ELEM(1, args) \
/**/

#define HAS_MEMBER_FUNCTION_SUBSTITUTE_PARAMETER( \
            args, param \
        ) \
    BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_ARRAY_ELEM(4, args)) \
    (U::*) \
    BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_ARRAY_ELEM(4, args)) \
/**/

#define HAS_MEMBER_FUNCTION(name, sig) \
    BOOST_MPL_HAS_MEMBER_IMPLEMENTATION( \
        ( 5, ( BOOST_PP_CAT(has_, name), name, 0, false, sig ) ) \
      , BOOST_MPL_HAS_MEMBER_INTROSPECT \
      , HAS_MEMBER_FUNCTION_SUBSTITUTE_PARAMETER \
      , HAS_MEMBER_FUNCTION_ACCESS \
    ) \
/**/

// Introspection for member static functions.
#define HAS_MEMBER_STATIC_FUNCTION_SUBSTITUTE_PARAMETER( \
            args, param \
        ) \
    BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_ARRAY_ELEM(4, args)) \
    (*) \
    BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_ARRAY_ELEM(4, args)) \
/**/

#define HAS_MEMBER_STATIC_FUNCTION(name, sig) \
    BOOST_MPL_HAS_MEMBER_IMPLEMENTATION( \
        ( 5, ( BOOST_PP_CAT(has_, name), name, 0, false, sig ) ) \
      , BOOST_MPL_HAS_MEMBER_INTROSPECT \
      , HAS_MEMBER_STATIC_FUNCTION_SUBSTITUTE_PARAMETER \
      , HAS_MEMBER_FUNCTION_ACCESS \
    ) \
/**/

// For example,
// HAS_MEMBER_FUNCTION(f, (void, (char)))
// expands to something like...
//
// template< typename T , typename fallback_ = boost::mpl::bool_< false > >
// class has_get_int {
// template< typename U >
// struct has_get_int_introspect {
// template< void (U::*) (char) >
// struct has_get_int_substitute {
// };

// template< typename V >
// static boost::mpl::aux::no_tag
// has_get_int_test(...);

// template< typename V >
// static boost::mpl::aux::yes_tag
// has_get_int_test(
// boost::mpl::aux::type_wrapper< V > const volatile*
// , has_get_int_substitute < &V::f >* = 0
// );

// static const bool value
// = sizeof(has_get_int_test< U >(0))
// == sizeof(boost::mpl::aux::yes_tag);
// typedef boost::mpl::bool_< value > type;
// };
// public:
// static const bool value = has_get_int_introspect< T >::value;
// typedef typename has_get_int_introspect< T >::type type;
// };

// Note that the 2nd arg for functions is a tuple representing the
// function signature.
HAS_MEMBER_TEMPLATE(t, 1)
HAS_MEMBER_FUNCTION(f, (void, (char)))
HAS_MEMBER_STATIC_FUNCTION(get_int, (int*, ()))

struct foo {
    template<class> struct t;
    void f(char);
    static int *get_int();
};

int main()
{
    BOOST_MPL_ASSERT((
        has_t<foo, char>
    ));
    BOOST_MPL_ASSERT((
        has_f<foo>
    ));
    BOOST_MPL_ASSERT((
        has_get_int<foo>
    ));
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk