// Copyright Tor Brede Vekterli 2008-2009 (vekterli@arcticinteractive.com) // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // Adaptation of Andrei Alexandrescu's abstract factory pattern, as outlined in // Modern C++ Design #ifndef BOOST_FACTORY_ABSTRACT_FACTORY_HPP #define BOOST_FACTORY_ABSTRACT_FACTORY_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "construct.hpp" #ifndef BOOST_ABSTRACT_FACTORY_MAX_PARAMS # define BOOST_ABSTRACT_FACTORY_MAX_PARAMS 10 #endif // Raising this number is currently bound to fail unless one also raises the // maximum arity for Fusion and probably several other libraries #ifndef BOOST_ABSTRACT_FACTORY_CREATE_MAX_ARITY # define BOOST_ABSTRACT_FACTORY_CREATE_MAX_ARITY 6 #endif #include "detail/abstract_factory_aux.hpp" namespace boost { namespace factory { // This field is used whenever no function signature is given (or if the // function arity specializations are insufficient, in which case a compile- // time MPL error will be given). For T == function-type specializations, see // detail/abstract_field_n.hpp template class abstract_factory_field { public: typedef BOOST_DEDUCED_TYPENAME detail::abstract_type_deduce::type abstract_type; typedef T result_type; typedef mpl::vector func_type; // For use with FunctionTypes et al protected: typedef mpl::int_<0> arity; typedef boost::fusion::vector<> fusion_seq_type; public: // FIXME! VC++ bugs out here if private is set virtual abstract_type* do_create( detail::type_tag, const fusion_seq_type&) const = 0; public: virtual ~abstract_factory_field() {} // Have a public creation method here for convenience in case the factory is // passed as a single-type abstract factory field. This method will be hidden // by any 0-ary subclasses, so it shouldn't cause any ambiguousness result_type create() const { return detail::wrap( do_create(detail::type_tag(), fusion_seq_type())); } // If you've gotten this error it means you've got a higher number of function // parameters than BOOST_ABSTRACT_FACTORY_CREATE_MAX_ARITY is set to. // Define BOOST_ABSTRACT_FACTORY_CREATE_MAX_ARITY to a desired number // before including any abstract factory-related headers and then try re- // compiling. // FIXME: this involves additional dependencies on other libraries that must // be documented BOOST_MPL_ASSERT_MSG((boost::is_function::value == false), BOOST_ABSTRACT_FACTORY_FUNCTION_ARITY_SET_TOO_LOW, (T*)); }; #define BOOST_PP_ITERATION_LIMITS (0, BOOST_ABSTRACT_FACTORY_CREATE_MAX_ARITY) #define BOOST_PP_FILENAME_1 "detail/abstract_field_n.hpp" #include BOOST_PP_ITERATE() #define BOOST_ABSTRACT_FACTORY_arg(z, n, na_type) \ typename BOOST_PP_CAT(T,n) = na_type // Note: despite the name, this actually generates a scatter-hierarchy, which // is necessary if we want to be able to independently pass factory fields to // functions et al without caring about its other fields. For an actual linear // hierarchy generation, see concrete_factory_hierarchy. // The in-memory size of an abstract_factory instance (or rather, of a // concrete_factory instance, as abstract_factory is, unsurprisingly, abstract) // is that of n vtable pointers, where n is the number of produceable types // (i.e. each additional type adds another vtable, due to the scattered // hierarchy approach) and assuming that the concrete factory holds no explicit // state of its own // The code complexity here is rather high due to the desire to offer "proper" // argument forwarding for non-C++1x compilers, meaning that the arguments must // be deduced from the abstract types rather than being simple r-value refs. template class abstract_factory : public mpl::inherit_linearly< BOOST_DEDUCED_TYPENAME detail::normalize_sequence< mpl::vector >::type , mpl::inherit > , mpl::empty_base >::type { public: typedef BOOST_DEDUCED_TYPENAME detail::normalize_sequence< mpl::vector >::type sequence_type; // Create a type-mapping from "undecorated" abstract type -> full abstract type. // i.e. if a sequence element is foo*(int,long), the mapping will be // foo -> foo*(int,long) typedef BOOST_DEDUCED_TYPENAME mpl::fold< sequence_type , mpl::map<> , mpl::insert< mpl::_1 , mpl::pair< detail::abstract_type_deduce , mpl::_2 > > >::type type_map_type; private: // Argument type lookups must be done indirectly in order to avoid errors // caused by invalid arities when the function overload set is SFINAE // instantiated template struct argtype_lookup_impl {}; template struct argtype_lookup_impl { typedef BOOST_DEDUCED_TYPENAME mpl::at< BOOST_DEDUCED_TYPENAME abstract_factory_field< BOOST_DEDUCED_TYPENAME mpl::at::type >::func_type , mpl::int_ // +1 since the first element is the result type >::type type; }; template struct argtype_lookup : argtype_lookup_impl< Abstract , N , ((boost::function_types::function_arity< BOOST_DEDUCED_TYPENAME detail::as_function_type< BOOST_DEDUCED_TYPENAME mpl::at::type >::type >::value) >= N) > {}; // Lookup and deduce the mapping abstract base type -> proper result type. // The proper result type may just be Abstract*, or it could be // auto_ptr, shared_ptr et al template struct result_type_lookup_and_deduce { typedef BOOST_DEDUCED_TYPENAME detail::result_type_deduce< BOOST_DEDUCED_TYPENAME mpl::at::type >::type type; }; public: #define BOOST_FACTORY_ABSTRACT_create_arg(z, n, unused) \ BOOST_DEDUCED_TYPENAME argtype_lookup::type BOOST_PP_CAT(arg, n) #define BOOST_FACTORY_ABSTRACT_create_fusion_arg(z, n, varname) \ BOOST_DEDUCED_TYPENAME field_t::fusion_arg ## n ## _type(varname ## n) // Lookup the type of the argument given in the function signature (if // present), and use that instead of templating on all args. This allows us // to take rvalues and still provide proper (non-)const propagation etc #define BOOST_FACTORY_ABSTRACT_create(z, n, unused) \ template \ BOOST_DEDUCED_TYPENAME result_type_lookup_and_deduce::type \ create(BOOST_PP_ENUM(n, BOOST_FACTORY_ABSTRACT_create_arg, ~)) const \ { \ typedef BOOST_DEDUCED_TYPENAME mpl::at::type full_t; \ typedef BOOST_DEDUCED_TYPENAME detail::result_type_deduce::type result_t; \ typedef abstract_factory_field field_t; \ const field_t& field = *this; \ return detail::wrap(field.do_create(detail::type_tag(), \ BOOST_DEDUCED_TYPENAME field_t::fusion_seq_type( \ BOOST_PP_ENUM(n, BOOST_FACTORY_ABSTRACT_create_fusion_arg, arg)))); \ } #define BOOST_PP_LOCAL_MACRO(n) BOOST_FACTORY_ABSTRACT_create(~, n, ~) #define BOOST_PP_LOCAL_LIMITS (0, BOOST_ABSTRACT_FACTORY_CREATE_MAX_ARITY) #include BOOST_PP_LOCAL_ITERATE() #undef BOOST_FACTORY_ABSTRACT_create_fusion_arg #undef BOOST_FACTORY_ABSTRACT_create_arg #undef BOOST_FACTORY_ABSTRACT_create }; namespace detail { //[af_operator_new_creator_impl // Due to the generic argument pack, this class works for all call-arities template class operator_new_creator_impl : public Super /*< Super is the superclass from which the class /must/ publicly inherit, as it effectively contains the rest of the abstract factory hierarchy. >*/ { private: typedef BOOST_DEDUCED_TYPENAME boost::remove_pointer< ConcreteType >::type concrete_type; typedef abstract_factory_field abstract_field_type; typedef BOOST_DEDUCED_TYPENAME abstract_field_type::abstract_type abstract_type; typedef BOOST_DEDUCED_TYPENAME abstract_field_type::fusion_seq_type fusion_seq_type; virtual abstract_type* do_create(type_tag, const fusion_seq_type& seq) const { return boost::fusion::invoke_function_object( new_ptr(), seq); /*< [^new_ptr] is a simple function object with 0-/n/ arity overloads of `operator()` which invoke `operator new`. See [@../../../../boost/factory/construct.hpp construct.hpp]. >*/ }; public: virtual ~operator_new_creator_impl() {} }; //] BOOST_MPL_HAS_XXX_TRAIT_DEF(factory_creator_tag); template < typename ConcreteType , typename AbstractType , typename DefaultCreator , typename Super > struct select_concrete_superclass_tagged { typedef BOOST_DEDUCED_TYPENAME mpl::apply< BOOST_DEDUCED_TYPENAME mpl::if_< has_factory_creator_tag , ConcreteType , DefaultCreator >::type , ConcreteType , AbstractType , Super >::type type; }; // When we're generating the actual concrete factory hierarchy, it must be // linear rather than scattered. The matching select_concrete_superclass // must provide a type that can take a superclass type parameter template < typename ConcreteSequence , typename AbstractSequence , typename DefaultCreator , typename Root > class concrete_factory_hierarchy : public mpl::inherit_linearly< mpl::zip_view< mpl::vector > , select_concrete_superclass_tagged< mpl::at > , mpl::at > , DefaultCreator , mpl::_1 > , Root > { }; } // namespace detail // Currently does not take an explicit template type--implicitly given to // metafunc. TODO: change this when setting default creators is figured out? //[af_operator_new_creator struct operator_new_creator { typedef void factory_creator_tag; template struct apply { typedef detail::operator_new_creator_impl< ConcreteType, AbstractType, Super > type; }; }; //] template < class AbstractFactory , BOOST_PP_ENUM( BOOST_ABSTRACT_FACTORY_MAX_PARAMS , BOOST_ABSTRACT_FACTORY_arg , detail::na )> class concrete_factory : public detail::concrete_factory_hierarchy< BOOST_DEDUCED_TYPENAME detail::normalize_sequence< mpl::vector >::type , BOOST_DEDUCED_TYPENAME AbstractFactory::sequence_type , operator_new_creator // TODO: make configurable , AbstractFactory >::type { typedef BOOST_DEDUCED_TYPENAME detail::normalize_sequence< mpl::vector >::type concrete_sequence_type; // Do a compile-time assertion to make sure both sequences are the same // length, as it's possible for the compile to succeed even though this // isn't the case (with undefined results) BOOST_MPL_ASSERT_MSG( (mpl::equal_to< mpl::size , mpl::size >::value) , ABSTRACT_AND_CONCRETE_SEQUENCE_SIZE_MISMATCH , (detail::assertion_helper< BOOST_DEDUCED_TYPENAME AbstractFactory::sequence_type> , concrete_sequence_type)); }; } // namespace factory } // namespace boost #endif // BOOST_FACTORY_ABSTRACT_FACTORY_HPP