Boost logo

Boost :

Subject: Re: [boost] Excellent factory style design pattern implementation
From: Tor Brede Vekterli (vekterli_at_[hidden])
Date: 2008-12-07 15:54:12


Hi

I'm not very familiar with Boost.Fusion, but as I'm currently working
on adding support for function signatures to the abstract factory
pattern (as per the current abstract_factory implementation in the
vault, which builds on top of Loki), I'm in need of a sequence I can
use to pack and forward constructor arguments so that there's no need
to create n overloads for the creation policies' functions (where n is
the number of arguments--needs just take a single non-polymorphic
argument pack which can invoke an arbitrary n-argument functor). How
does Fusion's sequence types deal with forwarding of references and
such? My current implementation uses a custom, highly simplified
argument packer which simply internally immutably stores and forwards
arguments with types in the exact same form as they were specified in
the function signature. Eg. for some_abstract_type*(int, const
std::string&) it will store and forward an int by value and
std::string by const reference. I am however not really sure if this
is at all optimal, because it leaves a lot of room for error if a user
simply forgets to specify some_really_large_type as a reference, in
which case it will be copied several times before it reaches the
actual construction code. But using all references internally seems to
open the code up for the good old forwarding problems when dealing
with rvalues (such as when doing
factory.create<some_abstract_type*(int, const_std::string&)>(1234,
my_string)), unless I'm mistaken. Ahh, dilemmas :) I'd be interested
in hearing how you used Fusion for your purposes.

I'm also very interested in input on how people would prefer an
abstract factory to offer its functionality to users (as well as the
design in general). Currently it allows code such as

typedef abstract_factory<
  mpl::vector<abstract_foo, abstract_bar*(const std::string&), abstract_baz*()>
> abstract_factory_t;
...
typedef concrete_factory<
    abstract_factory_t
  , mpl::vector<foo_impl, prototype<>, baz_impl>
> concrete_factory_t;

which states that both abstract_foo and abstract_baz implementation
objects should be created with their default constructors
(abstract_baz's function signature is empty and thus their end-effect
is the same), and that abstract_bar implementation objects should be
created with a constructor taking in a const std::string&. When no
function signature is present, specifying the type as a pointer is
voluntary, as it is otherwise implicitly given as a result type.
Concrete factories need not (and should not) specify any function
signatures for their types, as these are already directly available in
the abstract factory when a concrete type is matched up against its
corresponding abstract type (based simply on their sequence
positions). Is this an acceptable level of "magic", or will it cause
confusion?

The usage of MPL sequences in the external interfaces is something
that is used for ease of implementation, but I suspect this is also
something that many end-users will not necessarily be comfortable
with. Some Boost.Preprocessor action should be enough to reduce this
to abstract_factory<abstract_foo, abstract_bar....> et al, though.

Creation is done through create<abstract-type>(params matching
signature) member functions in the abstract factory type (as already
shown):

factory.create<some_abstract_type*(int, const_std::string&)>(1234, my_string);

which seems a bit overly verbose. The need to specify the entire type
in the template parameter is partially because I wanted to allow
people to specify the same abstract base type several times, but with
different constructor params (and mostly because it was the easiest,
but let's casually pretend that wasn't the main reason). When I think
about it, I'm not sure if this warrants the extra verbosity, however.
If it's a requirement that no abstract base classes be identical, it
should be possible to simply specify the abstract result-type itself,
i.e. factory.create<some_abstract_type>(1234, my_string), and let the
function figure out its parameters based on existing information
present in the type hierarchy. This is once again a tradeoff
nonverbosity<-->explicitness which has its pros and cons from both
perspectives.

As in Loki, due to their autogenerated class hierarchy abstract
factories can be broken down into--and passed as--individual fields,
decoupling the creation of single abstract types from their full
factory type:

void do_some_stuff(const abstract_factory_field<abstract_foo>& field)
{
  std::auto_ptr<abstract_foo> p(field.create());
  ...
}
...
do_some_stuff(my_concrete_factory);

Note that the concrete type of abstract_bar is prototype<>, which
means that a prototype object is used to create the new object, rather
than the default operator new. Prototypes are internally stored as
boost::shared_ptrs and assigned as follows:

my_concrete_factory.prototype<abstract_bar*(const std::string&)>(new
bar_impl(some_stuff));

with overloads for shared_ptr and auto_ptr.

When create<...>(...) is invoked with a type that is implemented as a
prototype, a clone operation is invoked on the stored object pointer.
Currently, the passed construction-arguments are completely ignored.
The clone operation, as well as what happens when there's no prototype
stored is policy-specifyable (defaults to calling object->clone() and
throwing an exception, respectively).

Maik,
I'll gladly add it to the sandbox, but I think I want to develop and
clean up the code a bit further first, as well as extend the unit test
coverage and try to get some proper BoostBook documentation up.

Vicente,
no worries, I simply hadn't looked closely enough at the existing
functional factory implementation to judge whether or not mine offered
the exact same functionality or not :) It seems the two should be able
to live side by side, fulfilling different needs.

Apologies for the somewhat fragmented reply.

Best regards,
Tor Brede Vekterli

On Thu, Dec 4, 2008 at 6:03 PM, Stjepan Rajko <stjepan.rajko_at_[hidden]> wrote:
> On Thu, Dec 4, 2008 at 4:35 AM, George van Venrooij
> <george.van.venrooij_at_[hidden]> wrote:
>>>
>>> Have you take a look on the Boost.Factory library from Tobias Schwinger
>>> accepted the 2007-12-30. This library has not been yet added to Boost, you
>>> can get it from the Vault.
>>>
>>
>> The Boost.Factory library seems to be something completely different. The
>> boost-centric-factory-pattern-implementation library is definitely something
>> I will try out as its implementation seems very complete and fits nicely
>> into what I plan to do in the near future.
>>
>
> Indeed, Boost.Factory is a set of function object adaptors for dynamic
> or value construction (it is also referred to as
> Boost.Functional/Factory). Very useful in it's own right, but
> different from the library the OP mentioned.
>
> I've used Boost.Functional/Factory in developing an object factory
> class that is much, much less developed than the mentioned
> boost-centric-factory-pattern-implementation - but in concert with
> Boost.Fusion, Boost.Factory made the implemenation rather
> straightforward. Perhaps the author of
> boost-centric-factory-pattern-implementation can find it useful to
> enhance his own library (which I also find interesting and would like
> to take a closer look when I get some time).
>
> Stjepan
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
>


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