|
Boost : |
From: Aleksey Gurtovoy (agurtovoy_at_[hidden])
Date: 2002-11-28 04:49:02
David B. Held wrote:
> Now, for a real stumper. It seems to me that VC6 is only
> pretending to compile MPL::Lambda. The reason being that it refuses
> to recognize nested types that have passed through lambda. Here's
> what I think is the relevant code:
[snip]
> Now, inside smart_ptr...
>
> typedef typename storage_policy_<T, policies_>::type
> storage_policy;
> typedef storage_policy::pointer_type pointer_type;
>
> VC6 really doesn't like the second typedef. It says:
>
> error C2039: 'pointer_type' : is not a member of '`global
> namespace''
> error C2146: syntax error : missing ';' before identifier
> 'pointer_type'
> error C2868: 'pointer_type' : illegal syntax for
> using-declaration;
> expected qualified-name
>
Actually, the errors have little to do with MPL in general or lambda in
particular; what you are seeing is called "early template instantiation"
(ETI), a known MSVC 6.5/7.0 bug that you inevitably have to deal with as
soon as your template code grows out of the "toy samples" category. In
particular, one manifestations of the ETI is that the compiler, while
_parsing_ something like
typedef typename storage_policy_<T, policies_>::type storage_policy;
typedef storage_policy::pointer_type pointer_type;
inside another class template definition, can choose, for its own internal
purposes, to substitute 'storage_policy' by 'int' type - and proceed with
further declarations. Naturally, in our case something like that renders the
'pointer_type' typedef ill-formed, and you get the errors you've seen.
Fortunately, once you know what's happening, the bug is easy to workaround:
template< typename T > struct pointer_type
{
typedef typename T::pointer_type type;
};
#if defined(BOOST_MPL_MSVC_ETI_BUG)
template<> struct pointer_type<int>
{
typedef int type;
};
#endif
typedef typename pointer_type<storage_policy>::type pointer_type;
Well, having said that, I should note that actually MPL's lambda on MSVC
(and Borland, currently) _does_ work slightly differently from its normal
counterpart, setting up some additional pitfalls. The most ugly one, that I
cannot workaround in the library, is that "parsing" a lambda expression like
the one below on compilers without partial specialization results in
_instantiation_ of the involved templates with the placeholder arguments:
template <class Policy>
struct get_category
{
typedef typename Policy::policy_category type;
BOOST_MPL_AUX_LAMBDA_SUPPORT(1,get_category,(Policy))
};
...
// on MSVC 6.5/7.0 'get_category<_>' is actually
// instantiated
typedef typename mpl::find_if<
Sequence
, is_same<get_category<_>, Category>
>::type iter_;
Since, naturally, '_' placeholder doesn't have a nested 'policy_category'
member, the above will be ill-formed as written when compiled on a
non-conforming compiler. To workaround it, you have to do something along
these lines:
template <class Policy>
struct get_category_impl
{
typedef typename Policy::policy_category type;
};
template <class Policy>
struct get_category
: mpl::if_<
mpl::is_placeholder<Policy>
, mpl::identity<Policy>
, get_category_impl<Policy>
>::type
{
BOOST_MPL_AUX_LAMBDA_SUPPORT(1,get_category,(Policy))
};
On an aside note, in general, unless you are very intimate with particular
compiler's idiosyncrasies, it much easier to debug a complex template code
on more or less conforming compiler (Comeau, Intel C++, GCC 3.2) and then
adopt it for MSVC and Borland, than fight with the last two while catching
your own bugs.
[...]
> Thanks for any insight you can provide.
Attached is a modified 'smart_ptr.hpp' that more or less compiles on MSVC
6.5, Intel C++ 6.0, and GCC 3.2 (see the test for the details). You'll need
the latest boost CVS snapshot for that.
HTH,
Aleksey
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk