|
Boost : |
From: Mat Marcus (mmarcus_at_[hidden])
Date: 2001-11-30 03:18:38
In this (long) post I hope to accomplish three goals.
1. Port another piece of Loki (part of HierarchyGenerators.h) to be
compiler neutral. It is not far from there to Loki's abstract
factory. Maybe abstract factory would be an interesting piece of
Loki to examine in the near future.
2. I would like to see boosters develop a "metaprogramming portability
standards" document. The idea would be to offer support to authors
who wish to produce metaprograms or metalibraries that are compiler
neutral. The intent is not to force anyone to follow the
guidelines, but rather to share our collected wisdom on how to
produce artifacts that are as widely usable as possible. I will
start the ball rolling by sharing my approach and collecting some
of the known techniques into one article. I am hoping that others
will take some time to write up some of their techniques too. If
there is interest I maintain the results in a guidelines document
in the files section.
3. Solicit help in porting more of Loki and other metaprogramming
libraries. Is anyone else interested in helping to bring these
libraries to VC?
I will try to accomplish these goals by describing the process of
porting part of Loki's HierarchyGenerator.h to be more compiler
neutral. Along the way I will characterize some key issues and
workarounds when porting metaprograms/metalibraries to Microsoft
Visual C++ 6. The port is not quite complete and has not been well
tested yet. But I hope to have something in the files section
soon. Andrei do you have an updated version of HierarchyGenerator.h?
Is there a test suite for it?
First let me observe the Loki license for GenScatterHierarchy and
Fields below:
/* The Loki Library Copyright (c) 2001 by Andrei Alexandrescu. This
code accompanies the book: Alexandrescu, Andrei. "Modern C++ Design:
Generic Programming and Design Patterns Applied". Copyright (c)
2001. Addison-Wesley. Permission to use, copy, modify, distribute
and sell this software for any purpose is hereby granted without
fee, provided that the above copyright notice appear in all copies
and that both that copyright notice and this permission notice
appear in supporting documentation. The author or Addison-Welsey
Longman make no representations about the suitability of this
software for any purpose. It is provided "as is" without express or
implied warranty. */
Loki provides some metafunctions that can be used to generate classes
from typelists. These are well documented in MC++D beginning on page
64. For the purposes of this article lets us examine the
GenScatterHierarchy metafunction:
//////////////////////////////////////////////////////////////////////////
// class template GenScatterHierarchy Generates a scattered hierarchy
// starting from a typelist and a template Invocation (TList is a
// typelist, Model is a template of one arg):
// GenScatterHierarchy<TList, Model> The generated class inherits all
// classes generated by instantiating the template 'Model' with the
// types contained in TList
// ///////////////////////////////////////////////////////////////////////
template <class TList, template <class> class Unit>
class GenScatterHierarchy;
template <class T1, class T2, template <class> class Unit>
class GenScatterHierarchy<Typelist<T1, T2>, Unit>
: public GenScatterHierarchy<T1, Unit>
, public GenScatterHierarchy<T2, Unit>
{
};
template <class AtomicType, template <class> class Unit>
class GenScatterHierarchy : public Unit<AtomicType>
{
};
template <template <class> class Unit>
class GenScatterHierarchy<NullType, Unit>
{
};
When porting to VC we have three main problems. The first problem is
that VC doesn't support template template parameters. The template
template parameter is used as a way to pass one metafunction as an
argument to another. But there is another well known idiom for this
(see Czarnecki and Eisenecker's seminal book "Generative Programming"
book for this and more). We can pass a metafunction as a member class
template inside of a struct wrapper. Let's call this the "metafunction
wrapper idiom" for brevity. So the refactoring here will look
something like this:
// First let us consider a typical calling sequence from MC++D p.65
template <class T>
struct Holder
{
T value_;
};
typedef GenScatterHierarchy<
TYPELIST_3(int, std::string, Widget), Holder>
WidgetInfo;
// Now we refactor it to use the "metafunction wrapper idiom"
template <class T>
struct Holder //sames as before
{
T value_;
};
struct HolderWrapper // metafunction wrapper idiom
{
template <class T>
struct Result {
typedef Holder<T> type;
};
};
typedef GenScatterHierarchy<
TYPELIST_3(int, std::string, Widget), HolderWrapper>
WidgetInfo;
Of course this changes the interface of GenScatterHierarchy. But there
are some people that would argue that the metafunction wrapper is
superior to a template template argument. (For example, one has more
control over default template arguments. There is a corresponding
change to GenScatterHierarchy's implementation:
// The original implementation :
template <class TList, template <class> class Unit>
class GenScatterHierarchy;
template <class T1, class T2, template <class> class Unit>
class GenScatterHierarchy<Typelist<T1, T2>, Unit>
: public GenScatterHierarchy<T1, Unit>
, public GenScatterHierarchy<T2, Unit>
{
};
template <class AtomicType, template <class> class Unit>
class GenScatterHierarchy : public Unit<AtomicType>
{
};
template <template <class> class Unit>
class GenScatterHierarchy<NullType, Unit>
{
};
// Changes to become:
template <class TList, class MetaFunctionWrapper>
class GenScatterHierarchy;
template <class T1, class T2, class MetaFunctionWrapper>
class GenScatterHierarchy<Typelist<T1, T2>, MetaFunctionWrapper>
: public GenScatterHierarchy<T1, MetaFunctionWrapper>
, public GenScatterHierarchy<T2, MetaFunctionWrapper>
{
};
template <class AtomicType, class MetaFunctionWrapper>
class GenScatterHierarchy : public
MetaFunctionWrapper::template Result<AtomicType>::type
{
};
template <class MetaFunctionWrapper>
class GenScatterHierarchy<NullType, MetaFunctionWrapper>
{
};
This brings us to the second porting problem. This code still won't
compile. The heinous VC "typedef dependent template parameter"
bug. This bug is difficult to describe but I will give it a brief
try. The code above:
MetaFunctionWrapper::template Result<AtomicType>::type
fails to compile. But other similar expressions succeed. Success seems
to occur in the cases when the outer class is not a template
parameter. There are a couple of workarounds for this but they are not
legal C++. Aleksey has encapsulated one of these in ::boost::mpl as
the BOOST_MPL_DEPENDENT_TEMPLATE_TYPEDEF macro. (Actually, the macro
needs a slight modification to work here: the last parameter must be
eliminated.). So we replace the above line of code with:
BOOST_MPL_DEPENDENT_TEMPLATE_TYPEDEF(MetaFunctionWrapper, \
Result, AtomicType)
This is admittedly ugly, but at least it only appears in the
implementation, not the client code. Many of us don't like macros too
much. We can do a little better by burying this hack away inside
another metafunction, let's call it Apply. That is:
template <class MetaFunctionWrapper, class T>
struct Apply
{
#ifdef BOOST_MSVC
// based on the (non-conforming) MSVC trick from MPL
template<bool>
struct MetaFunctionWrapper_VC : MetaFunctionWrapper {};
//illegal C++ which causes VC to admit that MetaFunctionWrapper_VC
//can have a nested template:
template<>
struct MetaFunctionWrapper_VC<true>
{template<class> struct Result; };
typedef typename MetaFunctionWrapper_VC<
::boost::mpl::detail::msvc_never_true<MetaFunctionWrapper>::value
>::template Result<T>::type type;
};
#else
typedef typename MetaFunctionWrapper::template Result<T>::type type;
#endif
};
Then the offending code:
template <class AtomicType, class MetaFunctionWrapper>
class GenScatterHierarchy : public
MetaFunctionWrapper::template Result<AtomicType>::type
{
};
becomes:
template <class AtomicType, class MetaFunctionWrapper>
class GenScatterHierarchy : public
typename Apply<MetaFunctionWrapper, AtomicType>::type
{
};
Our third and final task is to eliminate the use of partial
specialization. This problem is reasonably well understood, and we can
solve it without changing the metafunction interface. See for example
the articles in the boost files section in the Partial Specialization
folder, or the porter's notes in "typelist.hpp for VC 6", for
example. So I will let the code speak for itself here. We put it all
together for the final version below.
- Mat
//-------------------------
#include "boost/typelist.hpp"
#include "boost/mpl/mpl_config.hpp"
namespace boost {
template <class MetaFunctionWrapper, class T>
struct Apply
{
#ifdef BOOST_MSVC
// Modified version of the non-conforming MSVC trick from MPL
template<bool> struct MetaFunctionWrapper_VC :
MetaFunctionWrapper {};
template<> struct MetaFunctionWrapper_VC<true>
{ template<class> struct Result; };
typedef typename MetaFunctionWrapper_VC<
::boost::mpl::detail::msvc_never_true<MetaFunctionWrapper>::value
>::template Result<T>::type type;
#else
typedef typename MetaFunctionWrapper::template Result<T>::type type;
#endif
};
class EmptyType {};
namespace loki {
template <class TList, class MetaFunctionWrapper>
class GenScatterHierarchy;
namespace detail {
// Bring in is_tlist and is_null_typelist from ported typelist.hpp.
// The is_tlist<T> metafunction returns true is true iff T is a typelist.
// The is_null_typelist returns true iff T is a null_typelist.
// We use the case_tag idiom from my ported typelist.hpp. Alternatively,
// we could have used Czarnecki & Eisenecker's SWITCH metafunction...
// enum case_tag { null_typelist_case =100, general_typelist_case,
// atomic_case }; // start at 100 to avoid bool errors
template <class T, class U>
class InheritFromTwo : public T, public U
{
};
using namespace ::boost::loki::tl::detail;
template <case_tag tag>
struct GenScatterHierarchySpecialization;
template <>
struct GenScatterHierarchySpecialization<general_typelist_case>
{
template <class TList, class MetaFunctionWrapper>
struct Result
: public GenScatterHierarchy<typename TList::head,
MetaFunctionWrapper>
{
typedef
InheritFromTwo<
GenScatterHierarchy<typename TList::head,
MetaFunctionWrapper>,
GenScatterHierarchy<typename TList::tail,
MetaFunctionWrapper>
> type;
};
};
template <>
struct GenScatterHierarchySpecialization<atomic_case>
{
template <class AtomicType, class MetaFunctionWrapper>
struct Result
{
typedef typename
Apply<MetaFunctionWrapper, AtomicType>::type type;
};
};
template <>
struct GenScatterHierarchySpecialization<null_typelist_case>
{
template <class NullTypeList, class MetaFunctionWrapper>
struct Result
{
typedef EmptyType type;
};
};
} // end namespace detail
//For the sake of brevity in this article we use namespace detail:
template <class TList, class MetaFunctionWrapper>
class GenScatterHierarchy : public
// VC accepts this call since GenScatterHierarchy is not a template
// parameter
//$$ needs clean up, move to detail, and refactor:
detail::GenScatterHierarchySpecialization<
tl::detail::is_null_typelist<TList>::value ?
tl::detail::null_typelist_case :
tl::detail::is_tlist<TList>::value ?
tl::detail::general_typelist_case :
tl::detail::atomic_case>::
template Result<TList, MetaFunctionWrapper>::type
{
};
template <class T, class TList, class MetaFunctionWrapper>
typename Apply<MetaFunctionWrapper, T>::type&
Field(GenScatterHierarchy<TList, MetaFunctionWrapper>& obj)
{
return obj;
}
template <class T, class TList, class MetaFunctionWrapper>
const typename Apply<MetaFunctionWrapper, T>::type&
Field(const GenScatterHierarchy<TList, MetaFunctionWrapper>& obj)
{
return obj;
}
} //end namespace loki
}//end namespace boost
template <class T>
struct Holder //sames as before
{
T value_;
};
struct HolderWrapper // metafunction wrapper idiom
{
template <class T>
struct Result {
typedef Holder<T> type;
};
};
// Simplistic test
typedef ::boost::loki::GenScatterHierarchy<
TYPELIST_3(int, float, bool), HolderWrapper>
WidgetInfo;
WidgetInfo foo;
int i = ::boost::loki::Field<int>(foo).value_;
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk