Article Discussion
Subscribing Template Classes with Object Factories in C++
Summary: Object factories provide a useful abstraction for object construction. A special problem with object factories must be considered, however, when subscribing template classes with object factories. This article presents an overview of the "subscription problem" along with several solutions.
7 posts on 1 page.      
« Previous 1 Next »
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: September 24, 2007 8:31 PM by John
Frank
Posts: 135 / Nickname: fsommers / Registered: January 19, 2002 7:24 AM
Subscribing Template Classes with Object Factories in C++
September 14, 2007 1:40 PM      
Object factories provide a useful abstraction for object construction. A special problem with object factories must be considered, however, when subscribing template classes with object factories. This article presents an overview of the "subscription problem" along with several solutions:

http://www.artima.com/cppsource/subscription_problem.html

What do you think of the techniques presented in the article?
Maxim
Posts: 2 / Nickname: jazzer / Registered: September 16, 2007 5:21 PM
Re: Subscribing Template Classes with Object Factories in C++
September 16, 2007 10:31 PM      
I think preprocessor can help a lot in such tasks.

The code is very straightforward and looks almost exactly how you'd write it manually.
It also can work with any number of policies:

#include <boost/preprocessor/seq/for_each_product.hpp>
#include <boost/preprocessor/seq/enum.hpp>

#define S1 (NoHide)(TimeoutHidding)(CHP)
#define S2 (NoDefense)(BulletProofed)(ShellProofed)
#define S3 (Movable)(Stationary)
// etc.

#define REGISTER(r, policies) \
factory->subscribe(Radar<BOOST_PP_SEQ_ENUM(policies)>::ID(), \
createInstance<IRadar, Radar<BOOST_PP_SEQ_ENUM(policies)> >);

BOOST_PP_SEQ_FOR_EACH_PRODUCT(REGISTER, (S1)(S2)(S3)/*etc.*/)
Maxim Noah
Posts: 2 / Nickname: mempko / Registered: November 9, 2004 2:22 PM
Re: Subscribing Template Classes with Object Factories in C++
September 17, 2007 7:19 AM      
This is off subject but my name is Maxim Khailo. Yanchenko, are you Ukrainian? I certainly am.
Maxim
Posts: 2 / Nickname: jazzer / Registered: September 16, 2007 5:21 PM
Re: Subscribing Template Classes with Object Factories in C++
September 17, 2007 8:08 AM      
> This is off subject but my name is Maxim Khailo.
> Yanchenko, are you Ukrainian? I certainly am.
Hi tyozka :)
No, I'm Russian, but from Bryansk (so it's wild mixture of Russians, Ukrainians, Belorussians and whoever else lived there)
m
Posts: 3 / Nickname: mlimber / Registered: November 2, 2006 6:37 AM
Re: Subscribing Template Classes with Object Factories in C++
September 17, 2007 8:57 AM      
Thanks for this. I'd note that there are several syntax errors herein (e.g., "FactorySubscriber_2<C, H1, H2, IPC, Factory, CMW:>:subscribe(f)" should have the ":" after the ">").

I have a perhaps complementary technique that gives automatic registration with Loki factories. Below is a simplified version to ease explanation and understanding that is built with Loki 0.1.6 and MS VC++ 2005.

The key is that each factory-creatable class inherits from my FactoryCreatablePolicy, which automatically registers it with the factory. It comes with a virtual destructor (see notes in the destructor), but considering that we're talking about a hierarchy of classes, the factory-creatable class probably already had a virtual table, so we're not added much.


#include "loki/Factory.h"
#include "loki/Singleton.h"
#include "loki/static_check.h"
#include "loki/TypeManip.h"

template
<
class AbstractClass,
class ConcreteClass,
class FactoryIDType
>
class FactoryCreatablePolicy
{
public:
static AbstractClass* Create()
{
// Verify that the given types are
// actually super and sub classes
LOKI_STATIC_CHECK(
LOKI_SUPERSUBCLASS( AbstractClass, ConcreteClass ),
types_are_not_super_and_subclass );

return new ConcreteClass;
}

// Method to allow non-conformant compilers
// to manually register with the factory.
// Only needed as a work-around.
static bool IsRegistered()
{ return registered_; }

protected:
// Protected ctor and dtor so base classes
// can use this as a policy
FactoryCreatablePolicy() {}

// "virtual" is required because otherwise the compiler
// may optimize this away, subverting the trick described
// below.
virtual ~FactoryCreatablePolicy()
{
// In order to automatically register with
// the factory any subclass that uses this
// class as a policy, we must force the
// instantiation of the static member
// registered_. Otherwise, it will not be
// instantiated unless the program explicitly
// references it somewhere, which is an
// inconvenient requirement. This way it will
// always be instantiated on a standard-
// conformant compiler.

(void)&registered_;
}

private:
// This typedef would need to be expanded to
// include all other factory parameters. It is
// reduced here for the sake of simplicity of
// presentation.

typedef Loki::SingletonHolder
<
Loki::Factory
<
AbstractClass,
FactoryIDType
>
> theFactory;

static const volatile bool registered_;
};

// Register all our factory-creatable classes
// with their respective factories. (Note that
// these static members appear in the header file
// because they are themselves templates. The
// compiler will ensure that there is only one
// instance of each globally.)
template
<
class AbstractClass,
class ConcreteClass,
class FactoryIDType
>
const volatile bool
FactoryCreatablePolicy<AbstractClass, ConcreteClass, FactoryIDType>::registered_ =
theFactory::Instance().Register(
ConcreteClass::FACTORY_ID,
Create );


Now some code to demonstrate what it does. Given a hierarchy of classes (here, a base with two derived classes), each class must define a class ID for the factory. I have used ints here for simplicity, but it could be strings or whatever.


struct Base
{
virtual ~Base() {}
};

struct Derived1
: Base
, FactoryCreatablePolicy<Base,Derived1,int>
{
enum { FACTORY_ID = 1 };
};

struct Derived2
: Base
, FactoryCreatablePolicy<Base,Derived2,int>
{
enum { FACTORY_ID = 2 };
};

typedef Loki::Factory<Base, int> BaseFactory;
typedef Loki::SingletonHolder< BaseFactory > theBaseFactory;

int main()
{
BaseFactory& factory =
theBaseFactory::Instance();

try
{
auto_ptr<Base> b1( factory.CreateObject( Derived1::FACTORY_ID ) );
auto_ptr<Base> b2( factory.CreateObject( Derived2::FACTORY_ID ) );

// ...

cout << "Successful creation of objects." << endl;
}
catch( const exception& e )
{
cerr << e.what() << endl;
return -1;
}
}


This might be compatible and incorporatable into what you've done here. I've also proposed that the Loki crew consider including code in Loki.

Cheers! --M

PS, Thanks to several folks on comp.lang.c++.moderated, especially James Kanze, for helping me work this out a bit.
Maxim Noah
Posts: 2 / Nickname: mempko / Registered: November 9, 2004 2:22 PM
Re: Subscribing Template Classes with Object Factories in C++
September 18, 2007 8:06 AM      
> > This is off subject but my name is Maxim Khailo.
> > Yanchenko, are you Ukrainian? I certainly am.
> Hi tyozka :)
> No, I'm Russian, but from Bryansk (so it's wild mixture of
> Russians, Ukrainians, Belorussians and whoever else lived
> there)

I guess to be more specific, I am half Russian and half Ukrainian. The region I was born is was also a wild mix of Russians, Ukrainians and the like. However most people just call themselves Russian, and that is what I do.

I live in USA now and am excited to see anyone with the name "Maxim". Ha :-)
John
Posts: 2 / Nickname: jtorjo / Registered: September 24, 2007 0:18 AM
Re: Subscribing Template Classes with Object Factories in C++
September 24, 2007 8:31 PM      
The article is great, but what it's trying to address, is pretty far fetched...

Do we really want this kind of bloat? Do we really want to register 81, 144, 256 factories? I mean, I'm all for templates - if you've read some of my articles, you should definitely know, but this is taken a bit to the extreme... On top of this, we could add pretty high compile times...

Unless for the very simple cases, where the templated class is very small, and the policies are trivial, I would definitely choose a different solution: for each type of policy, I would have a Delegater, which would probably use a smart pointer to the real policy. So, for instance, instead of 2 x 4 x 5 = 40 classes, I'd have 2 + 4 + 5 = 11 classes. Instead of 3 x 3 x 3 x 3 = 81, I'd have 3 + 3 + 3 + 3 = 12.

That's my 2 euro cents, anyway ;)

Best,
John

--
http://John.Torjo.com -- C++ expert
... call me only if you want things done right
7 posts on 1 page.
« Previous 1 Next »