[mpl] zero argument member template detection

Hello, Following up on Steven's question from a few weeks ago (sorry for delay) about has_xxx_template, there is a bug in msvc 9 and 10 that causes ICE. I filed a bug against msvc here. https://connect.microsoft.com/VisualStudio/feedback/details/571720 Here is code to reproduce the bug. struct S { template<class T = int> struct X {}; }; template<class T> // The following line causes ICE void f(typename T::template X<>*) {} // The following alternative line compiles cleanly //void f(typename S::template X<>*) {} // The following line also compiles cleanly //void f(typename T::template X<int>*) {} int main() { f<S>(0); } Incidentally, the code above is accepted by msvc 8 and gcc... and clang! :) I do not know of a work around. Moreover, while looking for a work around, I found another bug in has_xxx_template that may render zero argument detection unreliable on any compiler. Rethinking some of my original design choices, I now believe that it is a bad idea to conflate member template detection and template arity detection. It would be better if BOOST_MPL_HAS_XXX_TEMPLATE_DEF defined a metafunction to detect member templates of any arity (up to some configurable limit). So I made the appropriate changes to effect this and updated the tests and documentation. The changes did not cause errors running the MPL test suite on gcc 3.4, 4.3, 4.4, 4.5 and msvc 7.1, 8, 9, 10. So, in sum, the interface is now cleaner and works consitently across compilers. Since this is the first release of has_xxx_template, I went ahead and checked the changes into trunk. Hopefully, I got this committed in time for the 1.44 code freeze... Do I still need to merge to release? Further discussion of some details follows for those who are interested. First, regarding the general problem of template arity detection, perhaps this is a motivating need for a seperate template_arity metafunction. There's already one in mpl/aux_ that's not part of the external library API. If there's interest, I'll post more on this later in a seperate thread. Regarding the specific problem of zero member template argument detection for has_xxx_template, the issue is that while you can instantiate a template with zero arguments, you do not actually declare a template with zero arguments, obviously. For example, struct S { template<class T = int> struct xxx {}; }; So, S::xxx is actually a one argument template that can be instatiated with zero args; e.g. S::xxx<>(). Originally, I was trying to detect how many arguments a template can be instatiated with, but I think this is the wrong approach. The technique I'm using in BOOST_MPL_HAS_XXX_TEMPLATE_DEF can only reliably detect the declared arity of the tempate. Here's a brief outline of how it works. BOOST_MPL_HAS_XXX_TEMPLATE_DEF defines a metafunction that uses SFINAE to detect member templates in the following way. template<template<class> class> struct substitute {}; template<class T> long has_xxx_test(substitute< T::template xxx >*); template<class> short has_xxx_test(...); const bool result = sizeof(has_xxx_test<S>(0)) == sizeof(long); However, to detect zero arguments, the following would be invalid, since you cannot declare a template with zero parameters. template<template<> class> struct substitute {}; So instead the zero arg test was performed using template<class T> long has_xxx_test(typename T::template xxx<>*); Unfortunately, this test causes ICE on msvc, and really, it doesn't completely work on any compiler, since it is invalid if T's xxx member template does not have default parameters; i.e. it cannot properly reject member templates requiring one or more arguments. The new BOOST_MPL_HAS_XXX_TEMPLATE_DEF on trunk resolves these issues by removing the requirement that users specify the template arity and arguments. Instead, it generates a metafunction that detects member templates of any arity. It does so by providing several overloads of the internal test function, one for each arity, up to a user configurable maximum (BOOST_MPL_LIMIT_METAFUNCTION_ARITY) as illustrated by the following simplified code. #include <boost/mpl/assert.hpp> #include <boost/mpl/bool.hpp> template<class T> class has_xxx { template<template<class> class> struct substitute1 {}; template<template<class,class> class> struct substitute2 {}; template<class U> static long has_xxx_test(substitute1<U::template xxx>*); template<class U> static long has_xxx_test(substitute2<U::template xxx>*); template<class> static short has_xxx_test(...); public: static const bool value = sizeof(has_xxx_test<T>(0)) == sizeof(long); typedef boost::mpl::bool_<value> type; }; struct S1 { template<class> struct xxx {}; }; struct S2 { template<class,class> struct xxx {}; }; int main() { BOOST_MPL_ASSERT(( has_xxx<S1> )); BOOST_MPL_ASSERT(( has_xxx<S2> )); BOOST_MPL_ASSERT_NOT(( has_xxx<int> )); } Daniel Walker
participants (1)
-
Daniel Walker