Proposal: Automatic registration for Factory

Developers
mlimber
2007-09-17
2013-04-08
  • mlimber
    mlimber
    2007-09-17

    I read a recent article on subscribing with Loki factories (<http://www.artima.com/cppsource/subscription_problem.html>), but I wanted to suggest an alternate approach. 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 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 destructor.

    #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;
      }
    }

    Is it worth jazzing up to support all the extras of Loki's Factory (e.g., creation parameters, error policies, etc.) so that it can be included 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.

     
    • I think that's a great trick. I haven't found a problem with it. How portable is it in practice? Have you tried it on a number of platforms?

      Self-registration was always a weak point with Loki's Factory. I'd be happy to see that fixed. Subject to the approval of the other admins (Peter and Rich), I'd like to check this change in. Would you be kind to submit an updated header? If not, I'll write one with credit.

      One note - probably "creatable" could be improved upon. How about FactorySelfRegistering?

      Thanks,

      Andrei

       
    • Andrei wrote:

      > Self-registration was always a weak point with Loki's Factory. I'd be happy to see that
      > fixed. Subject to the approval of the other admins (Peter and Rich), I'd like to check
      > this change in. Would you be kind to submit an updated header? If not, I'll write one
      > with credit.

      I copy-n-pasted this code into a .cpp file and guess what.  It compiled without errors on Microsoft's Visual Studio 8 compiler and ran without crashing, and running correctly.  Now, I like seeing that in submitted code!  No errors, no fuss.  Just correct behavior first time without having to modify a single line of code.

      I'd like to see this in Loki as well.  I can see either adding it to Factory.h or making a new header file.  (Not all that picky about where to add it.)

      Assuming that Peter likes this also, when it gets added to Loki, I do want to see the usual documentation comments added for each class and function.  Yeah, I like doxygen-style comments.

      Before it goes into Loki, I'd like to see what a "jazzed up" version looks like with creation parameters and error policies.  I really want to see a jazzed-up version that builds on top of the existing Loki factory nicely.

      mlimber, can you also create some tests which we can add to the test cases for the Factory?  The sample you provided here makes a good starting point for those tests.  Can you also make some tests which show how not to use it - such as using a factory to make a type it does not know about.

      Thanks,

      Rich

      P.S. - Nicely done!

       
  • mlimber
    mlimber
    2011-04-25

    Danger: zombie thread rises!

    So apparently, I didn't see these messages back when they came out. Mea culpa. What's your interest in pursuing this now? Is Loki still active? I see there's a proposed Boost.Factory that is modeled on Loki but doesn't have this auto-registration functionality.