Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2006-09-25 16:03:12


Sohail Somani wrote:
>> [mailto:boost-bounces_at_[hidden]] On Behalf Of Eric Niebler
>>
>> I have come up with a little generic object construction
>> utility using
>> Boost.Parameter that works for me. It allows you to non-intrusively
>> associate named parameters with a type for the purpose of
>> construction,
>> and a way to forward a (possibly filtered) argument pack on to
>> sub-objects. Is there general interest in such a utility?
>
> This sounds like it would be generally useful for factory functions as
> well... Is that true?

Well, it *is* a factory function, but a generic one. OK, there seems to
be interest. Here's an dumb example, involving a facade that associates
a name with an object:

   template<typename Object>
   struct named_facade {
     typedef Object object_type;
     named_facade( /*TODO*/ )
       : object_( /*TODO*/ )
       , name_( /*TODO*/ )
     {}
   private:
     Object object_;
     std::string name_;
   };

Now, we want a named vector:

   template<typename T>
   struct named_vector
     : named_facade<std::vector<T> >
   {
     named_vector(
         size_t size
       , std::string name
       , T const & value = T()
     )
       : named_facade<std::vector<T> >( /*TODO*/ )
     {}
   };

First, we define named parameters for use when constructing a std::vector:

   BOOST_PARAMETER_KEYWORD(tag, size)
   BOOST_PARAMETER_KEYWORD(tag, value)

And we hook the factory for std::vector:

   namespace boost { namespace constructors { namespace impl
   {
     struct std_vector_tag;

     template<typename T, typename Al>
     struct tag<std::vector<T,Al> >
     {
       typedef std_vector_tag type;
     };

     template<typename T>
     struct construct<T, std_vector_tag>
     {
       typedef parameter::parameters<
           parameter::required<tag::size>
         , parameter::optional<tag::value>
> args_type;

       typedef T result_type;

       template<typename Args>
       T operator()(Args const &args) const
       {
         return T(args[size], args[value | typename T::value_type()];
       }
     };
   }}}

With that, we can now construct a vector with:

   constructors::construct<std::vector<int> >(
     size = 10, value = 42);

Nothing too exciting, yet. Next, we specify the constructor arguments to
named_facade< >:

   // first, define a keyword for the name parameter
   BOOST_PARAMETER_KEYWORD(tag, name)

   namespace boost { namespace constructors { namespace impl
   {
     struct named_facade_tag;

     template<typename Object>
     struct tag<named_facade<Object> >
     {
       typedef named_facade_tag type;
     };

     template<typename T>
     struct construct<T, named_facade_tag>
       : constructors::arg_pack_construct
     {
       // NOTE: by inheriting from arg_pack_constructor, the
       // construct<named_facade<...> >(...) function will return an
       // argument pack instead of a named_facade<>. We will define
       // a named_facade<> constructor that takes an argument pack.

       typedef typename T::object_type object_type;
       // TODO: what we really want here is to insert the "name"
       // keyword into object_type's parameter spec, but we can't
       // do that yet because parameters<> is not an MPL sequence:
       typedef typename construct<object_type>::args_type args_type;
     };
   }}}

Now, we can complete the implementation of named_facade:

   template<typename Object>
   struct named_facade {
     typedef Object object_type;

     template<typename ArgPack>
     named_facade( ArgPack const & args )
       : object_(
           // forwards the args to Object's c'tor
           constructors::forward_construct<Object>(args)
         )
       , name_( args[ name | "<noname>" ] )
     {}
   private:
     Object object_;
     std::string name_;
   };

Finally, the named_vector constructor looks like:

   template<typename T>
   struct named_vector
     : named_facade<std::vector<T> >
   {
     typedef named_facade<std::vector<T> > base_type;

     named_vector(
         size_t size_
       , std::string name_
       , T const & value_ = T()
     )
       : base_type(
           constructors::construct<base_type>(
               size = size_
             , value = value_
             , name = name_
         )
     {}
   };

Basically, you define specialization of the construct<> template that
either:

A) returns a fully constructed T object via the associated named
parameters, or
B) returns an argument pack, assuming T has a constructor that accepts
an argument pack.

(A) is useful when we must make an existing type fit into the framework.
(B) is useful when we must pass constructor arguments through to
sub-objects, or when in-place construction is important (eg., if the
object is expensive to copy, or is noncopyable)

The code lives at http://tinyurl.com/hoowm for now.

-- 
Eric Niebler
Boost Consulting
www.boost-consulting.com

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