Boost logo

Boost :

Subject: [boost] [mpl] zero argument member template detection
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2010-07-07 16:52:30


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


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