augment the singleton factory to create a subclass.
Helps avoiding dependency problems, because client code is no longer coupled to the implementation class
This commit is contained in:
parent
250954bb5f
commit
e2ee8f081b
6 changed files with 338 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,6 +4,7 @@
|
|||
.[^.]*
|
||||
*.os
|
||||
*.gch
|
||||
,valgrind.log*
|
||||
Buildhelper.pyc
|
||||
optcache
|
||||
Makefile.in
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ This code is heavily inspired by
|
|||
#include "common/util.hpp"
|
||||
#include "nobugcfg.h"
|
||||
|
||||
//#include <boost/bind.hpp>
|
||||
|
||||
|
||||
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> class Create = singleton::StaticCreate, // how to create/destroy the instance
|
||||
template <class> class Life = singleton::AutoDestroy, // how to manage Singleton Lifecycle
|
||||
template <class> class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!!
|
||||
< class SI // the class of the Singleton instance
|
||||
, template <class> class Create = singleton::StaticCreate // how to create/destroy the instance
|
||||
, template <class> class Life = singleton::AutoDestroy // how to manage Singleton Lifecycle
|
||||
, template <class> class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!!
|
||||
>
|
||||
class SingletonFactory
|
||||
{
|
||||
|
|
|
|||
161
src/common/singletonsubclass.hpp
Normal file
161
src/common/singletonsubclass.hpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
SINGLETONSUBCLASS.hpp - variant of the singleton (factory) creating a subclass
|
||||
|
||||
Copyright (C) CinelerraCV
|
||||
2007, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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 <boost/scoped_ptr.hpp>
|
||||
#include <typeinfo>
|
||||
|
||||
|
||||
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<template<class> 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<class S>
|
||||
struct TypedLink : Link
|
||||
{
|
||||
virtual S* create () { return POL<S>::create (); } // covariance checked!
|
||||
virtual void destroy (I* pSi) { POL<S>::destroy (static_cast<S*> (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> link;
|
||||
|
||||
/** Forwarding Template to configure the basic SingletonFactory */
|
||||
template<class II>
|
||||
struct Adapted
|
||||
{
|
||||
static II* create () { return link->create (); }
|
||||
static void destroy (II* pSi) { link->destroy (pSi); }
|
||||
};
|
||||
};
|
||||
|
||||
template<template<class> class A, class I>
|
||||
scoped_ptr<typename Adapter<A,I>::Link> Adapter<A,I>::link (0);
|
||||
|
||||
|
||||
/** type-information used to configure the factory instance
|
||||
* with the concrete implementation type to be created. */
|
||||
template<class SU>
|
||||
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> class Create = singleton::StaticCreate // how to create/destroy the instance
|
||||
, template <class> class Life = singleton::AutoDestroy // how to manage Singleton Lifecycle
|
||||
, template <class> class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!!
|
||||
>
|
||||
class SingletonSubclassFactory
|
||||
: public SingletonFactory< SI
|
||||
, singleton::Adapter<Create,SI>::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<class SU>
|
||||
SingletonSubclassFactory (singleton::UseSubclass<SU>)
|
||||
{
|
||||
typedef typename singleton::Adapter<Create,SI> Adapter;
|
||||
typedef typename Adapter::template TypedLink<SU> 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
|
||||
|
|
@ -118,6 +118,15 @@ out: '§&Ω%€' --> ''
|
|||
END
|
||||
|
||||
|
||||
TEST "SingletonSubclass_test" SingletonSubclass_test 13 <<END
|
||||
out: using the Singleton should create TargetObj(13)...
|
||||
out: ctor TargetObj(13) successfull
|
||||
out: calling a non-static method on the Singleton-Implementation
|
||||
out: .....TargetObj(13): data="*************", array[13]={0,1,2,3,4,5,6,7,8,9,10,11,12,}
|
||||
out: dtor ~TargetObj(13) successfull
|
||||
END
|
||||
|
||||
|
||||
TEST "SingletonTestMock_test" SingletonTestMock_test <<END
|
||||
out: TestSingletonO::doIt() call=1
|
||||
out: TestSingletonO::doIt() call=2
|
||||
|
|
@ -131,7 +140,6 @@ out: TestSingletonO::doIt() call=3
|
|||
END
|
||||
|
||||
|
||||
|
||||
TEST "Singleton_test" Singleton_test 23 <<END
|
||||
out: testing TargetObj(23) as Singleton(statically allocated)
|
||||
out: ctor TargetObj(23) successfull
|
||||
|
|
|
|||
163
tests/components/common/singletonsubclasstest.cpp
Normal file
163
tests/components/common/singletonsubclasstest.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
SingletonSubclass(Test) - actually creating a subclass of the Singleton Type
|
||||
|
||||
Copyright (C) CinelerraCV
|
||||
2007, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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 <boost/lexical_cast.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
|
||||
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<Interface>;
|
||||
friend class singleton::HeapCreate<Interface>;
|
||||
};
|
||||
|
||||
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<uint>(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<Impl> typeinfo;
|
||||
|
||||
// define an instance of the Singleton factory,
|
||||
// spezialized to create the concrete Type passed in
|
||||
SingletonSubclassFactory<Interface> 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<Impl_XXX> more_special_type;
|
||||
|
||||
try
|
||||
{
|
||||
SingletonSubclassFactory<Interface> 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<Unrelated> yet_another_type;
|
||||
// SingletonSubclassFactory<Interface> instance (yet_another_type);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SingletonSubclass_test, "unit common");
|
||||
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace cinelerra
|
||||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
|
|
|
|||
Loading…
Reference in a new issue