detecting static member function

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. So far I have tried the following (testing if "static int *get_int()" is declared): typedef char yes_type; typedef struct {char a[2]; } no_type; template<int * (*f)()> struct test_get_int1 { }; template<class T> yes_type get_int_tester(test_get_int1<&T::get_int>*); template<class T> no_type get_int_tester(...); template<class T> struct test_get_int_base { BOOST_STATIC_CONSTANT(bool, value= (sizeof(get_int_tester<T>(0)) == sizeof(yes_type))); }; template<class T> struct test_get_int : boost::mpl::bool_<test_get_int_base<T>::value> { }; this works on some compilers : msvc7.1, msvc8.0, gcc3.4, gcc4.1.0, gcc4.1.2 but not on gcc3.2: when get_int is not present it complains "get_int is not member of type..." and strangely not on gcc4.1.1: when get_int is present it complains "static int *T::get_int cannot be a template parameter because is not of external linkage" I have not tried other compilers but I expect more of the same problems. Can anyone suggest a better way of doing this? Thanks in advance, Nikolay Mladenov

On 7/26/07, Nikolay Mladenov <nikolay.mladenov@gmail.com> 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> )); }
participants (2)
-
Daniel Walker
-
Nikolay Mladenov