From a1bb9178f5880ba707fb7239aa8deaa5036f68cf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 11 Sep 2014 00:10:59 +0200 Subject: [PATCH] Ticket #388: start investigation of MultiFact design needs overhaul, since current design leads to problems with GCC 4.8 onwards (and is messed up anyway) --- src/lib/muttifac.hpp | 277 +++++++++++++++++++++ tests/40core.tests | 6 +- tests/library/multifact-singleton-test.cpp | 137 ++++++++++ tests/library/multifact-test.cpp | 4 +- 4 files changed, 421 insertions(+), 3 deletions(-) create mode 100644 src/lib/muttifac.hpp create mode 100644 tests/library/multifact-singleton-test.cpp diff --git a/src/lib/muttifac.hpp b/src/lib/muttifac.hpp new file mode 100644 index 000000000..d36ff81dd --- /dev/null +++ b/src/lib/muttifac.hpp @@ -0,0 +1,277 @@ +/* + MULTIFACT.hpp - flexible family-of-object factory template + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file multifact.hpp + ** Framework for building a configurable factory, to generate families of related objects. + ** These building blocks are targeted towards the "classical" factory situation: obtaining + ** objects of various kinds, which are related somehow (usually through an common interface). + ** The creation of these objects might be non-trivial, while the number of flavours to be + ** produced and the exact parametrisation isn't known beforehand and needs to be figured out + ** at runtime. As a solution, thus a number of "fabrication lines" is set up, to be selected + ** on invocation through an ID (which may be symbolic, hashed or structural). + ** + ** Usually, the issue of object and storage management is closely related, while it is + ** desirable to keep the object production logic clean of these rather technical concerns. + ** The implementation built here separates the latter into a policy template invoked as a + ** \em wrapper, accepting the raw product and either registering it, taking ownership, clone + ** it or use it for more involved wiring. Obviously, the product generated by the installed + ** "fabrication lines" needs to be delivered in a form acceptable by the concrete wrapper; + ** mismatch will be spotted by the compiler on registration of the respective fabrication + ** function. + ** + ** \par Singleton generation + ** For the very common situation of building a family of singleton objects, accessible by ID, + ** there is a convenience shortcut: The nested MultiFact::Singleton template can be instantiated + ** within the context providing the objects (usually a static context). In itself a lib::Singleton + ** factory, it automatically registers the singleton access function as "fabrication" function + ** into a suitable MultiFact instance passed in as ctor parameter. + ** + ** @note there is an extension header, multifact-arg.hpp, which provides template specialisations + ** for the special case when the fabrication functions need additional invocation arguments. + ** @todo still way to convoluted design. We can do better //////////TICKET #388 + ** + ** @see multifact-test.cpp + ** @see multifact-argument-test.cpp + ** @see SingletonFactory + */ + + +#ifndef LIB_MUTTIFACT_H +#define LIB_MUTTIFACT_H + + +#include "lib/error.hpp" +#include "lib/depend.hpp" +#include "util.hpp" + +#include +#include +#include + + + +namespace lib { + namespace factory { + + // Helpers to wrap the factory's product + + /** + * Dummy "wrapper", + * just returning a target-ref + */ + template + struct PassReference + { + typedef TAR& RType; + typedef TAR& PType; + + PType wrap (RType object) { return object; } + }; + + + /** + * Wrapper taking ownership, + * by wrapping into smart-ptr + */ + template + struct BuildRefcountPtr + { + typedef TAR* RType; + typedef std::shared_ptr PType; + + PType wrap (RType ptr) { return PType{ptr}; } + }; + + + + + + /** + * Table of registered production functions for MultiFact. + * Each stored function can be accessed by ID and is able + * to fabricate a specific object, which is assignable to + * the nominal target type in the MultiFact definition. + */ + template + struct Fab + { + typedef std::function FactoryFunc; + + + FactoryFunc& + select (ID const& id) + { + if (!contains (id)) + throw lumiera::error::Invalid("unknown factory product requested."); + + return producerTable_[id]; + } + + void + defineProduction (ID const& id, FactoryFunc fun) + { + producerTable_[id] = fun; + } + + + /* === diagnostics === */ + + bool empty () const { return producerTable_.empty(); } + bool contains (ID id) const { return util::contains (producerTable_,id); } + + private: + std::map producerTable_; + }; + + + + /** + * @internal configuration of the elements + * to be combined into a MultiFact instance + */ + template< typename TY + , template class Wrapper + > + struct FabWiring + : Wrapper + { + typedef typename Wrapper::PType WrappedProduct; + typedef typename Wrapper::RType FabProduct; + typedef FabProduct SIG_Fab(void); + }; + + + + /** + * Factory for creating a family of objects by ID. + * The actual factory functions are to be installed + * from the usage site through calls to #defineProduction . + * Each generated object will be treated by the Wrapper template, + * allowing for the generation of smart-ptrs. The embedded class + * Singleton allows to build a family of singleton objects; it is + * to be instantiated at the call site and acts as singleton factory, + * accessible through a MultiFact instance as frontend. + */ + template< typename TY + , typename ID + , template class Wrapper + > + class MuttiFac + : public FabWiring + { + typedef FabWiring _Conf; + typedef typename _Conf::SIG_Fab SIG_Fab; + typedef Fab _Fab; + + _Fab funcTable_; + + + protected: + typedef typename _Fab::FactoryFunc Creator; + + Creator& + selectProducer (ID const& id) + { + return funcTable_.select(id); + } + + + public: + typedef typename _Conf::WrappedProduct Product; + + Product + operator() (ID const& id) + { + Creator& func = this->selectProducer (id); + return this->wrap (func()); + } + + Product + invokeFactory (ID const& id) ///< alias for the function operator + { + return this->operator() (id); + } + + + /** to set up a production line, + * associated with a specific ID + */ + template + void + defineProduction (ID id, FUNC fun) + { + funcTable_.defineProduction (id, fun); + } + + + /** + * Convenience shortcut for automatically setting up + * a production line, to fabricate a singleton instance + * of the given implementation target type (IMP) + */ + template + class Singleton + : lib::Depend + { + typedef lib::Depend SingFac; + + Creator + createSingleton_accessFunction() + { + return std::bind (&SingFac::operator() + , static_cast(this)); + } + + public: + Singleton (MuttiFac& factory, ID id) + { + factory.defineProduction(id, createSingleton_accessFunction()); + } + }; + + + /* === diagnostics === */ + + bool empty () const { return funcTable_.empty(); } + bool contains (ID id) const { return funcTable_.contains (id); } + }; + + + + } // namespace factory + + + + /** + * Standard configuration of the family-of-object factory + * @todo this is rather guesswork... find out what the best and most used configuration could be.... + */ + template< typename TY + , typename ID + > + class MuttiFact + : public factory::MuttiFac + { }; + + +} // namespace lib +#endif diff --git a/tests/40core.tests b/tests/40core.tests index b1bb40df1..8e6e98eef 100644 --- a/tests/40core.tests +++ b/tests/40core.tests @@ -376,7 +376,11 @@ return: 0 END -TEST "configurable Factory" MultiFact_test < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/multifact.hpp" +#include "lib/util.hpp" + +#include +#include +#include + + + +namespace lib { +namespace test{ + + using boost::lexical_cast; + using lib::test::showSizeof; + using util::isSameObject; + using util::isnil; + using std::ostream; + using std::string; + using std::cout; + using std::endl; + + using lumiera::error::LUMIERA_ERROR_INVALID; + + + namespace { // hierarchy of test dummy objects + + struct Interface + { + virtual ~Interface() {}; + virtual operator string () =0; + }; + + inline ostream& operator<< (ostream& os, Interface& ifa) { return os << string(ifa); } + + + enum theID + { ONE = 1 + , TWO + , THR + , FOU + }; + + typedef factory::MultiFact TestFactory; + + + template + class Implementation + : public Interface + { + operator string() + { + return "Impl-"+lexical_cast (ii); + } + + public: + static theID getTypeID() { return ii; } + }; + + /** Factory instance for the tests... */ + TestFactory theFact; + + // Configure the products to be fabricated.... + TestFactory::Singleton > holder1 (theFact,ONE); + TestFactory::Singleton > holder2 (theFact,TWO); + TestFactory::Singleton > holder3 (theFact,THR); + TestFactory::Singleton > holder4 (theFact,FOU); + } + + + + + + /***************************************************************//** + * @test verify simple setup of the MultiFact template. + * Define a hierarchy of test dummy objects, in order to + * register them automatically for creation through a suitable + * instantiation of MultiFact. Verify we get the correct product + * when invoking this MultiFac flavour. + * @see lib::MultiFact + */ + class MultiFactSingleton_test : public Test + { + void + run (Arg) + { + cout << theFact(ONE) << endl; + cout << theFact(TWO) << endl; + cout << theFact(THR) << endl; + cout << theFact(FOU) << endl; + cout << showSizeof (theFact) << endl; + + Interface & o1 = theFact(ONE); + Interface & o2 = theFact(ONE); + CHECK (isSameObject(o1,o2)); + + TestFactory anotherFact; + CHECK (isnil (anotherFact)); + VERIFY_ERROR (INVALID, anotherFact(ONE) ); + + TestFactory::Singleton > anotherSingletonHolder (anotherFact,ONE); + Interface & o3 = anotherFact(ONE); + CHECK (isSameObject(o2,o3)); + } + }; + + + /** Register this test class... */ + LAUNCHER (MultiFactSingleton_test, "unit common"); + + + +}} // namespace lib::test diff --git a/tests/library/multifact-test.cpp b/tests/library/multifact-test.cpp index 5bd7b3185..e8a0be3ad 100644 --- a/tests/library/multifact-test.cpp +++ b/tests/library/multifact-test.cpp @@ -23,7 +23,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -#include "lib/multifact.hpp" +#include "lib/muttifac.hpp" #include "lib/util.hpp" #include @@ -65,7 +65,7 @@ namespace test{ , FOU }; - typedef factory::MultiFact TestFactory; + typedef factory::MuttiFac TestFactory; template