diff --git a/.gitignore b/.gitignore index ef277c1a1..5dc912d9e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .[^.]* *.os *.gch +,valgrind.log* Buildhelper.pyc optcache Makefile.in diff --git a/src/common/singletonfactory.hpp b/src/common/singletonfactory.hpp index ba2151584..a0f63c978 100644 --- a/src/common/singletonfactory.hpp +++ b/src/common/singletonfactory.hpp @@ -40,7 +40,6 @@ This code is heavily inspired by #include "common/util.hpp" #include "nobugcfg.h" -//#include namespace cinelerra @@ -53,10 +52,10 @@ namespace cinelerra * @note internally uses static fields, so all functor instances share pInstance_ */ template - < class SI, // the class to make Singleton - template class Create = singleton::StaticCreate, // how to create/destroy the instance - template class Life = singleton::AutoDestroy, // how to manage Singleton Lifecycle - template class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!! + < class SI // the class of the Singleton instance + , template class Create = singleton::StaticCreate // how to create/destroy the instance + , template class Life = singleton::AutoDestroy // how to manage Singleton Lifecycle + , template class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!! > class SingletonFactory { diff --git a/src/common/singletonsubclass.hpp b/src/common/singletonsubclass.hpp new file mode 100644 index 000000000..90b26a6b5 --- /dev/null +++ b/src/common/singletonsubclass.hpp @@ -0,0 +1,161 @@ +/* + SINGLETONSUBCLASS.hpp - variant of the singleton (factory) creating a subclass + + Copyright (C) CinelerraCV + 2007, 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 singletonsubclass.hpp + ** Spezialized SingletonFactory creating sublcasses of the nominal type. + ** The rationale is to be able to defer the decision what type to create + ** down to the point where the singleton factory is actualy created. + ** Thus the code using the singleton need not know the implementation + ** class, but nevertheless gets an non-virtual access function to the + ** singleton instance (which can be inlined), and the compiler is + ** still able to spot type errors. Maybe someone knows a less + ** contrieved solution fulfilling the same criteria....? + ** + ** @see configrules.cpp usage example + ** @see SingletonSubclass_test + */ + + +#ifndef CINELERRA_SINGLETONSUBCLASS_H +#define CINELERRA_SINGLETONSUBCLASS_H + + +#include "common/singleton.hpp" + +#include +#include + + +namespace cinelerra + { + using boost::scoped_ptr; + + + namespace singleton + { + /** + * Helper template to use the general policy classes of the cinelerra::Singleton, + * but change the way they are parametrized on-the-fly. + */ + template class POL, class I> + struct Adapter + { + + struct Link + { + virtual ~Link() {} + virtual I* create () = 0; ///< @note compiler will check the actual type is asignable... + virtual void destroy (I* pSi) = 0; + }; + + template + struct TypedLink : Link + { + virtual S* create () { return POL::create (); } // covariance checked! + virtual void destroy (I* pSi) { POL::destroy (static_cast (pSi)); } + }; + + /** we configure this link \i later, when the singleton factory + * is actually created, to point at the desired implementation subclass. + */ + static scoped_ptr link; + + /** Forwarding Template to configure the basic SingletonFactory */ + template + struct Adapted + { + static II* create () { return link->create (); } + static void destroy (II* pSi) { link->destroy (pSi); } + }; + }; + + template class A, class I> + scoped_ptr::Link> Adapter::link (0); + + + /** type-information used to configure the factory instance + * with the concrete implementation type to be created. */ + template + struct UseSubclass + { }; + + } // namespace singleton + + + + + /** + * Special variant of the SingletonFactory with the option of actually creating + * a subclass or wrap the product in some way. For the user code, it should behave + * exactly like the standard SingletonFactory. The configuration of the product + * actually to be created is delayed until the ctor call, so it can be hidden + * away to the implementaton of a class using the SingletonFactory. + * + * @see configrules.cpp usage example + */ + template + < class SI // the class to use as Interface for the Singleton + , template class Create = singleton::StaticCreate // how to create/destroy the instance + , template class Life = singleton::AutoDestroy // how to manage Singleton Lifecycle + , template class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!! + > + class SingletonSubclassFactory + : public SingletonFactory< SI + , singleton::Adapter::template Adapted + , Life + , Threading + > + { + public: + /** The singleton-factory ctor configures what concrete type to create. + * It takes type information passed as dummy parameter and installes + * a trampoline object in the static field of class Adapter to perform + * the necessary up/downcasts. This allows to use whatever policy + * class ist wanted, but parametrizes this policy template with + * the concrete type to be created. (only the "create" policy + * needs to know the actual class, because it allocates storage) + */ + template + SingletonSubclassFactory (singleton::UseSubclass) + { + typedef typename singleton::Adapter Adapter; + typedef typename Adapter::template TypedLink TypedLink; + + if (!Adapter::link) + Adapter::link.reset (new TypedLink); + +#ifdef DEBUG + else + REQUIRE ( typeid(*Adapter::link) == typeid(new TypedLink), + "If using several instances of the sub-class-creating " + "singleton factory, all *must* be configured to create " + "objects of exactly the same implementation type!"); +#endif + } + }; + + + + +} // namespace cinelerra + +#endif diff --git a/tests/50components.tests b/tests/50components.tests index c0addaf23..12e0fb0c3 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -118,6 +118,15 @@ out: '§&Ω%€' --> '' END +TEST "SingletonSubclass_test" SingletonSubclass_test 13 < + + 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 "common/testtargetobj.hpp" +#include "common/singletonsubclass.hpp" + +#include "common/test/run.hpp" +#include "common/util.hpp" + +#include +#include +#include + +using boost::lexical_cast; +using boost::format; +using util::isnil; +using std::string; +using std::cout; + + +namespace cinelerra + { + namespace test + { + + + /** + * Target object to be instantiated as Singleton + * Allocates a variable amount of additional heap memory + * and prints diagnostic messages. + */ + class Interface : public TestTargetObj + { + public: + static int cnt; + static void setCountParam (uint c) { Interface::cnt = c; } + + virtual string identify() { return "Interface"; } + + protected: + Interface () : TestTargetObj(cnt) {} + virtual ~Interface() {} + + friend class singleton::StaticCreate; + friend class singleton::HeapCreate; + }; + + int Interface::cnt = 0; + + + class Impl : public Interface + { + public: + virtual string identify() { return "Implementation"; } + }; + + + // for checking the safety..... + class Impl_XXX : public Impl { }; + class Unrelated { }; + + + + + /******************************************************************* + * @test spezialized variant of the Singleton Factory, for creating + * subclasses (implementation classes) without coupling the + * caller to the concrete class type. + * Expected results: an instance of the subclass is created. + * @see cinelerra::Singleton + * @see cinelerra::SingletonSubclassFactory + * @see cinelerra::singleton::Adapter + */ + class SingletonSubclass_test : public Test + { + + virtual void run(Arg arg) + { + uint num= isnil(arg)? 1 : lexical_cast(arg[1]); + + cout << format("using the Singleton should create TargetObj(%d)...\n") % num; + + Interface::setCountParam(num); + + // marker to declare the concrete type to be created + singleton::UseSubclass typeinfo; + + // define an instance of the Singleton factory, + // spezialized to create the concrete Type passed in + SingletonSubclassFactory instance (typeinfo); + + // Now use the Singleton factory... + // Note: we get the Base type + Interface& t1 = instance(); + Interface& t2 = instance(); + + ASSERT ( &t1 == &t2, "not a Singleton, got two different instances." ); + + cout << "calling a non-static method on the Singleton-" + << t1.identify() << "\n" + << string (t1) << "\n"; + +#ifdef DEBUG + verify_error_detection (); +#endif + } + + + + void verify_error_detection () + { + + singleton::UseSubclass more_special_type; + + try + { + SingletonSubclassFactory instance (more_special_type); + cout << "was able to re-configure the SingletonSubclassFactory " + "with another type. This should be detected in debug mode\n"; + } + catch (...) + { + ASSERT (cinelerra_error () == error::CINELERRA_ERROR_ASSERTION); + } + + + // Note: the following won't compile, because the "subclass" isn't a subclass... + // + // singleton::UseSubclass yet_another_type; + // SingletonSubclassFactory instance (yet_another_type); + } + }; + + + + /** Register this test class... */ + LAUNCHER (SingletonSubclass_test, "unit common"); + + + + } // namespace test + +} // namespace cinelerra diff --git a/tests/components/common/typelistutiltest.cpp b/tests/components/common/typelistutiltest.cpp index 0d5fcabd1..d91edfe60 100644 --- a/tests/components/common/typelistutiltest.cpp +++ b/tests/components/common/typelistutiltest.cpp @@ -26,7 +26,6 @@ #include #include -#include using std::string; using std::cout;