WIP: draft an improved version of the Singleton factory
...this would both improve our general design and circumvent the problems with Clang and static variables
This commit is contained in:
parent
843d75ac2a
commit
567ab3819b
11 changed files with 550 additions and 114 deletions
126
src/lib/depend.hpp
Normal file
126
src/lib/depend.hpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
DEPENDENCY.hpp - access point to singletons and dependencies
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, 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.
|
||||
|
||||
====================================================================
|
||||
This code is heavily inspired by
|
||||
The Loki Library (loki-lib/trunk/include/loki/Singleton.h)
|
||||
Copyright (c) 2001 by Andrei Alexandrescu
|
||||
Loki code accompanies the book:
|
||||
Alexandrescu, Andrei. "Modern C++ Design: Generic Programming
|
||||
and Design Patterns Applied".
|
||||
Copyright (c) 2001. Addison-Wesley. ISBN 0201704315
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef LIB_DEPEND_H
|
||||
#define LIB_DEPEND_H
|
||||
|
||||
|
||||
#include "lib/nobug-init.hpp"
|
||||
#include "lib/sync-classlock.hpp"
|
||||
|
||||
namespace lib {
|
||||
|
||||
/**
|
||||
* Access point to singletons and other kinds of dependencies.
|
||||
* Actually this is a Factory object, which is typically placed into a static field
|
||||
* of the Singleton (target) class or some otherwise suitable interface.
|
||||
* @note internally uses static fields, so all factory instances share pInstance_
|
||||
* @note there is an ongoing discussion regarding Double Checked Locking pattern,
|
||||
* which in this case boils down to the question: does \c pthread_mutex_lock/unlock
|
||||
* constitute a memory barrier, such as to force any memory writes done within
|
||||
* the singleton ctor to be flushed and visible to other threads when releasing
|
||||
* the lock? To my understanding, the answer is yes. See
|
||||
* http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap04.html#tag_04_10
|
||||
* @param SI the class of the Singleton instance
|
||||
* @param Create policy defining how to create/destroy the instance
|
||||
* @param Life policy defining how to manage Singleton Lifecycle
|
||||
*/
|
||||
template<class SI>
|
||||
class Depend
|
||||
{
|
||||
typedef lib::ClassLock<SI> ThreadLock;
|
||||
|
||||
static SI* volatile pInstance_;
|
||||
|
||||
|
||||
public:
|
||||
/** Interface to be used by SingletonFactory's clients.
|
||||
* Manages internally the instance creation, lifecycle
|
||||
* and access handling in a multithreaded context.
|
||||
* @return "the" single instance of class S
|
||||
*/
|
||||
SI&
|
||||
operator() ()
|
||||
{
|
||||
if (!pInstance_)
|
||||
{
|
||||
ThreadLock guard;
|
||||
|
||||
if (!pInstance_)
|
||||
{
|
||||
if (isDead_)
|
||||
{
|
||||
Life<SI>::onDeadReference();
|
||||
isDead_ = false;
|
||||
}
|
||||
pInstance_ = Create<SI>::create();
|
||||
Life<SI>::scheduleDelete (&destroy);
|
||||
} }
|
||||
ENSURE (pInstance_);
|
||||
ENSURE (!isDead_);
|
||||
return *pInstance_;
|
||||
}
|
||||
|
||||
private:
|
||||
/** @internal helper used to delegate destroying the single instance
|
||||
* to the Create policy, at the same time allowing the Life policy
|
||||
* to control the point in the Application lifecycle when the
|
||||
* destruction of this instance occurs.
|
||||
*/
|
||||
static void destroy()
|
||||
{
|
||||
REQUIRE (!isDead_);
|
||||
Create<SI>::destroy (pInstance_);
|
||||
pInstance_ = 0;
|
||||
isDead_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Storage for SingletonFactory's static fields...
|
||||
template<class SI>
|
||||
SI* volatile Depend<SI>::pInstance_;
|
||||
|
||||
|
||||
|
||||
///// Question: can we get rid of the static fields?
|
||||
///// this is tricky because of invoking the destructors. If we rely on instance vars,
|
||||
///// the object may already have been released when the runtime system calls the
|
||||
///// destructors of static objects at shutdown.
|
||||
///// It seems this would either cost us much of the flexibility or get complicated
|
||||
///// to a point where we could as well implement our own Dependency Injection Manager.
|
||||
///// But the whole point of my pervasive use of this singleton template is to remain
|
||||
///// below this borderline of integration ("IoC yes, but avoid DI").
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
45
src/lib/dependency-factory.cpp
Normal file
45
src/lib/dependency-factory.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
DependencyFactory - managing the lifecycle of singletons and dependencies
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, 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 dependency-factory.cpp
|
||||
** This compilation unit holds the common backend of all singleton and dependency factories.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "lib/dependency-factory.hpp"
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
|
||||
namespace { // private implementation details...
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
|
||||
|
||||
|
||||
}// lib
|
||||
48
src/lib/dependency-factory.hpp
Normal file
48
src/lib/dependency-factory.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
DEPENDENCY-FACTORY.hpp - managing the lifecycle of singletons and dependencies
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, 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.
|
||||
|
||||
====================================================================
|
||||
This code is heavily inspired by
|
||||
The Loki Library (loki-lib/trunk/include/loki/Singleton.h)
|
||||
Copyright (c) 2001 by Andrei Alexandrescu
|
||||
Loki code accompanies the book:
|
||||
Alexandrescu, Andrei. "Modern C++ Design: Generic Programming
|
||||
and Design Patterns Applied".
|
||||
Copyright (c) 2001. Addison-Wesley. ISBN 0201704315
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef LIB_DEPENDENCY_FACTORY_H
|
||||
#define LIB_DEPENDENCY_FACTORY_H
|
||||
|
||||
|
||||
#include "lib/depend.hpp"
|
||||
|
||||
//#include "lib/nobug-init.hpp"
|
||||
//#include "lib/sync-classlock.hpp"
|
||||
|
||||
namespace lib {
|
||||
|
||||
/** */
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
|
||||
/** @file multifact.hpp
|
||||
** Framework for building a configurable factory, to generae families of related objects.
|
||||
** 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
|
||||
|
|
@ -51,7 +51,6 @@
|
|||
** @see multifact-test.cpp
|
||||
** @see multifact-argument-test.cpp
|
||||
** @see SingletonFactory
|
||||
** @see lib::factory::Factory
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
** There is a small number of different possibilities to handle execution
|
||||
** and UNDO of proc-Layer commands. Each of these is defined as a subclass
|
||||
** in this header and then hard wired into a small table. Handling patterns
|
||||
** are stateless singleton objects, thus we build using SingletonSubclass
|
||||
** are stateless singleton objects, thus we build using multiple Singleton
|
||||
** factory objects and configure them hard wired with the respective
|
||||
** implementation classes. The index positions in this table match
|
||||
** the sequence within the enum HandlingPattern::ID; all of this
|
||||
|
|
|
|||
256
tests/library/dependency-factory-test.cpp
Normal file
256
tests/library/dependency-factory-test.cpp
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
DependencyFactory(Test) - verify modes of creating singletons and dependencies
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, 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 "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "lib/depend.hpp"
|
||||
#include "test-target-obj.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
using ::Test;
|
||||
using util::isSameObject;
|
||||
|
||||
namespace {
|
||||
|
||||
const uint MAX_ID = 1000;
|
||||
|
||||
struct Sub
|
||||
: TestTargetObj
|
||||
{
|
||||
static uint created = 0;
|
||||
uint instanceID_;
|
||||
|
||||
Sub()
|
||||
: TestTargetObj(created++)
|
||||
,instanceID_(rand() % MAX_ID)
|
||||
{ }
|
||||
|
||||
operator string() const
|
||||
{
|
||||
return showType(*this)
|
||||
+ TestTargetObj::operator string();
|
||||
}
|
||||
};
|
||||
|
||||
struct SubSub
|
||||
: Sub
|
||||
{ };
|
||||
|
||||
struct SubSubSub
|
||||
: SubSub
|
||||
{ };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* @test verify the various modes of creating dependencies.
|
||||
*
|
||||
* @see lib::Dependency
|
||||
* @see Singleton_test
|
||||
*/
|
||||
class DependencyFactory_test : public Test
|
||||
{
|
||||
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
UNIMPLEMENTED ("dependency creation");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_defaultSingletonCreation()
|
||||
{
|
||||
Depend<Sub> accessor1;
|
||||
Depend<Sub> accessor2;
|
||||
|
||||
Sub & o1 = accessor1();
|
||||
Sub & o2 = accessor2();
|
||||
CHECK (isSameObject (o1, o2));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_renewal()
|
||||
{
|
||||
Depend<Sub> accessor1;
|
||||
Depend<Sub> accessor2;
|
||||
uint id1 = accessor1().instanceID_;
|
||||
CHECK (id1 == accessor2().instanceID_);
|
||||
|
||||
accessor1.shutdown();
|
||||
|
||||
Sub & o2 = accessor2();
|
||||
uint id2 = accessor2().instanceID_;
|
||||
CHECK (id1 != id2);
|
||||
|
||||
Sub & o1 = accessor1();
|
||||
CHECK (isSameObject (o1, o2));
|
||||
CHECK (id2 == accessor1().instanceID_);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_SubclassCreation()
|
||||
{
|
||||
Depend<SubSub> specialAccessor(buildSingleton<SubSubSub>());
|
||||
Depend<Sub> genericAccessor;
|
||||
|
||||
SubSub& oSub = specialAccessor();
|
||||
Sub& o = genericAccessor();
|
||||
|
||||
CHECK (!isSameObject (oSub, o));
|
||||
CHECK ( INSTANCEOF (SubSubSub, &oSub));
|
||||
CHECK (!INSTANCEOF (SubSubSub, &o));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_FactoryDefinition_is_sticky()
|
||||
{
|
||||
Depend<SubSub> otherSpecialAccessor;
|
||||
|
||||
SubSub& oSub = otherSpecialAccessor();
|
||||
CHECK ( INSTANCEOF (SubSubSub, &oSub));
|
||||
|
||||
otherSpecialAccessor.shutdown();
|
||||
|
||||
Depend<SubSub> yetAnotherSpecialAccessor;
|
||||
|
||||
SubSub& yetAnotherInstance = yetAnotherSpecialAccessor();
|
||||
CHECK ( INSTANCEOF (SubSubSub, &yetAnotherInstance));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_customFactory()
|
||||
{
|
||||
Depend<SubSubSub> customisedAccessor(useFactory (&customFactoryFunction));
|
||||
Depend<SubSub> otherSpecialAccessor;
|
||||
|
||||
SubSub& oSub = otherSpecialAccessor();
|
||||
SubSubSub& oSubS = customisedAccessor();
|
||||
|
||||
CHECK (!isSameObject (oSub, oSubS));
|
||||
CHECK ( INSTANCEOF (SubSubSub, &oSub));
|
||||
CHECK ( INSTANCEOF (SubSubSub, &oSubS));
|
||||
|
||||
CHECK (oSub.instanceID_ != oSubS.instanceID_);
|
||||
CHECK (MAX_ID + 10 == oSubS.instanceID_);
|
||||
}
|
||||
|
||||
static SubSubSub*
|
||||
customFactoryFunction (void)
|
||||
{
|
||||
SubSubSub* newObject = DependencyFactory::createSingleton<SubSubSub>();
|
||||
newObject->instanceID_ = MAX_ID + 10;
|
||||
return newObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
verify_temporaryReplacement()
|
||||
{
|
||||
Depend<Sub> genericAccessor;
|
||||
Sub& original = genericAccessor();
|
||||
uint oID = original.instanceID_;
|
||||
|
||||
genericAccessor.injectReplacement (new SubSubSub);
|
||||
|
||||
Sub& replacement = genericAccessor();
|
||||
uint repID = replacement.instanceID_;
|
||||
|
||||
CHECK (!INSTANCEOF (SubSubSub, &original));
|
||||
CHECK ( INSTANCEOF (SubSubSub, &replacement));
|
||||
CHECK (!isSameObject (original, replacement));
|
||||
|
||||
CHECK (oID != repID);
|
||||
CHECK (oID == original.instanceID_);
|
||||
|
||||
Depend<SubSub> special;
|
||||
Depend<SubSubSub> custom;
|
||||
|
||||
CHECK(!isSameObject (replacement, special() ));
|
||||
CHECK(!isSameObject (replacement, custom() ));
|
||||
|
||||
genericAccessor.injectReplacement (NULL);
|
||||
|
||||
Sub& nextFetch = genericAccessor();
|
||||
CHECK (isSameObject (original, nextFetch));
|
||||
CHECK (oID == nextFetch.instanceID_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
verify_automaticReplacement()
|
||||
{
|
||||
Depend<Sub> genericAccessor;
|
||||
Sub& original = genericAccessor();
|
||||
uint oID = original.instanceID_;
|
||||
|
||||
{
|
||||
Use4Test<SubSub> withinThisScope;
|
||||
|
||||
Sub& replacement = genericAccessor();
|
||||
uint repID = replacement.instanceID_;
|
||||
|
||||
CHECK (!INSTANCEOF (SubSub, &original));
|
||||
CHECK ( INSTANCEOF (SubSub, &replacement));
|
||||
CHECK (!INSTANCEOF (SubSubSub, &replacement));
|
||||
CHECK (!isSameObject (original, replacement));
|
||||
|
||||
Depend<Sub> anotherAccessor;
|
||||
Sub& otherAccess = anotherAccessor();
|
||||
CHECK (isSameObject (replacement, otherAccess));
|
||||
CHECK (repID == otherAccess.instanceID_);
|
||||
CHECK (repID == replacement.instanceID_);
|
||||
CHECK ( oID == original.instanceID_);
|
||||
}
|
||||
|
||||
Sub& nextFetch = genericAccessor();
|
||||
CHECK (isSameObject (original, nextFetch));
|
||||
CHECK (oID == nextFetch.instanceID_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
LAUNCHER (DependencyFactory_test, "unit common");
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
#include "lib/format-string.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "testtargetobj.hpp"
|
||||
#include "test-target-obj.hpp"
|
||||
#include "lib/singleton-subclass.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "lib/format-string.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "testtargetobj.hpp"
|
||||
#include "test-target-obj.hpp"
|
||||
#include "lib/singleton.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
using std::tr1::function;
|
||||
using boost::lexical_cast;
|
||||
using util::isSameObject;
|
||||
using util::_Fmt;
|
||||
using util::isnil;
|
||||
using std::string;
|
||||
|
|
@ -120,7 +121,7 @@ namespace test{
|
|||
TargetObj& t1 = instance();
|
||||
TargetObj& t2 = instance();
|
||||
|
||||
CHECK ( &t1 == &t2, "not a Singleton, got two different instances." );
|
||||
CHECK (isSameObject(t1, t2), "not a Singleton, got two different instances." );
|
||||
|
||||
cout << "calling a non-static method on the Singleton instance\n"
|
||||
<< string (t1) << "\n";
|
||||
|
|
|
|||
|
|
@ -47,30 +47,35 @@ namespace test{
|
|||
*/
|
||||
class TestSingletonO
|
||||
{
|
||||
int callCnt;
|
||||
Symbol typid;
|
||||
format msg;
|
||||
int callCnt_;
|
||||
Symbol typid_;
|
||||
format msg_;
|
||||
|
||||
public:
|
||||
TestSingletonO(Symbol ty="TestSingletonO")
|
||||
: callCnt (0), typid(ty), msg ("%s::doIt() call=%d\n")
|
||||
{
|
||||
TRACE (test, "ctor %s", typid.c());
|
||||
}
|
||||
virtual ~TestSingletonO()
|
||||
{
|
||||
TRACE (test, "dtor %s", typid.c());
|
||||
}
|
||||
: callCnt_(0)
|
||||
, typid_(ty)
|
||||
, msg_("%s::doIt() call=%d\n")
|
||||
{
|
||||
TRACE (test, "ctor %s", typid_.c());
|
||||
}
|
||||
|
||||
virtual
|
||||
~TestSingletonO()
|
||||
{
|
||||
TRACE (test, "dtor %s", typid_.c());
|
||||
}
|
||||
|
||||
void doIt ()
|
||||
{
|
||||
++callCnt;
|
||||
cout << msg % typid % callCnt;
|
||||
}
|
||||
{
|
||||
++callCnt_;
|
||||
cout << msg_ % typid_ % callCnt_;
|
||||
}
|
||||
|
||||
int getCnt ()
|
||||
{
|
||||
return callCnt;
|
||||
}
|
||||
{
|
||||
return callCnt_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -80,8 +85,7 @@ namespace test{
|
|||
*/
|
||||
struct Mock_1 : TestSingletonO
|
||||
{
|
||||
Mock_1() : TestSingletonO("Mock_1")
|
||||
{};
|
||||
Mock_1() : TestSingletonO("Mock_1") { };
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -89,8 +93,7 @@ namespace test{
|
|||
*/
|
||||
struct Mock_2 : TestSingletonO
|
||||
{
|
||||
Mock_2() : TestSingletonO("Mock_2")
|
||||
{};
|
||||
Mock_2() : TestSingletonO("Mock_2") { };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
TESTTARGETOBJ.hpp - a test (stub) target object for testing the factories
|
||||
TEST-TARGET-OBJ.hpp - a test (stub) target object for testing the factories
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
|
@ -21,28 +21,26 @@
|
|||
*/
|
||||
|
||||
|
||||
#ifndef LUMIERA_TEST_TESTTARGETOBJ_H
|
||||
#define LUMIERA_TEST_TESTTARGETOBJ_H
|
||||
#ifndef LIBRARY_TEST_TARGET_OBJ_H
|
||||
#define LIBRARY_TEST_TARGET_OBJ_H
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using boost::algorithm::join;
|
||||
using boost::lexical_cast;
|
||||
using boost::format;
|
||||
using std::string;
|
||||
using std::cout;
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
using util::_Fmt;
|
||||
using boost::lexical_cast;
|
||||
using std::string;
|
||||
using std::cout;
|
||||
|
||||
/**
|
||||
* Target object to be created by Test-Factories or as Singleton.
|
||||
* Allocates a variable amount of additional heap memory
|
||||
|
|
@ -51,28 +49,28 @@ namespace test{
|
|||
class TestTargetObj
|
||||
{
|
||||
uint cnt_;
|
||||
string* heapData_;
|
||||
string* heapArray_;
|
||||
string* heapData_;
|
||||
string* heapArray_;
|
||||
|
||||
public:
|
||||
TestTargetObj(uint num);
|
||||
~TestTargetObj() throw();
|
||||
virtual ~TestTargetObj();
|
||||
|
||||
operator string () const;
|
||||
virtual operator string () const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline
|
||||
TestTargetObj::TestTargetObj(uint num)
|
||||
: cnt_ (num),
|
||||
heapData_ (new string(num,'*')),
|
||||
heapArray_ (new string[num])
|
||||
{
|
||||
for (uint i=0; i<cnt_; ++i)
|
||||
heapArray_[i] = lexical_cast<string>(i);
|
||||
cout << format("ctor TargetObj(%i) successful\n") % cnt_;
|
||||
}
|
||||
: cnt_ (num)
|
||||
, heapData_ (new string(num,'*'))
|
||||
, heapArray_ (new string[num])
|
||||
{
|
||||
for (uint i=0; i<cnt_; ++i)
|
||||
heapArray_[i] = lexical_cast<string>(i);
|
||||
cout << _Fmt("ctor TargetObj(%i) successful\n") % cnt_;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
|
|
@ -80,7 +78,7 @@ namespace test{
|
|||
{
|
||||
delete heapData_;
|
||||
delete[] heapArray_;
|
||||
cout << format("dtor ~TargetObj(%i) successful\n") % cnt_;
|
||||
cout << _Fmt("dtor ~TargetObj(%i) successful\n") % cnt_;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -93,11 +91,8 @@ namespace test{
|
|||
array_contents += heapArray_[i]+",";
|
||||
array_contents+="}";
|
||||
|
||||
return str (format(".....TargetObj(%1%): data=\"%2%\", array[%1%]=%3%")
|
||||
% cnt_
|
||||
% *heapData_
|
||||
% array_contents
|
||||
);
|
||||
return _Fmt(".....TargetObj(%1%): data=\"%2%\", array[%1%]=%3%")
|
||||
% cnt_ % *heapData_ % array_contents;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1966,58 +1966,21 @@ Explicit placements are just created and never mutated, but copying and storage
|
|||
It would thus be desirable to have a fixed-sized allocation, able to hold the placement body as well as the (fixed) locating pins inline.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="Factories" modifier="Ichthyostega" created="200708100401" modified="201003160211" tags="impl discuss excludeMissing rewrite">
|
||||
<pre>We use Factories
|
||||
* for centralizing [[memory management|MemoryManagement]]
|
||||
* to support polymorphism (of course...)
|
||||
<div title="Factories" modifier="Ichthyostega" created="200708100401" modified="201310132317" tags="def Concepts" changecount="6">
|
||||
<pre>The use of factories separates object creation, configuration and lifecycle from the actual usage context. Hidden behind a factory function
|
||||
* memory management can be delegated to a centralised facility
|
||||
* complex internal details of service implementation can be turned into locally encapsulated problems
|
||||
* the actual implementation can be changed, e.g. by substituting a test mock
|
||||
|
||||
!Requirements
|
||||
* request the actual placement/allocation from the backend
|
||||
* allways hand out a smart-pointer
|
||||
* encapsulate / make configurable the smart-pointer type
|
||||
* install a callback into the smart-pointer for destroying the resource.
|
||||
* redirect the destroying request to the backend
|
||||
!common usage pattern
|
||||
Within Lumiera, factories are frequently placed as a static field right into the //service interface.// Common variable names for such an embedded factory are {{{instance}}} or {{{create}}}.
|
||||
The actual fabrication function is defined as function operator -- this way, the use of the factory reads like a simple function call. At usage site, only the reference to the //interface// or //kind of service// is announced.
|
||||
|
||||
!Implementation Questions
|
||||
* how much genericity? (Ichthyo is rather inclined not to overdo this one. Often it is preferable to have repeated implementations follow a well known pattern, if this leads to short and simple implementations, while the complete general solution will be much more contrived).
|
||||
* how to specify the actual type needed?
|
||||
* how to implement the cases where a subtype needs to be selected (abstract factory pattern). Embody this into the Factory, pass it in as a Strategy or treat the Factory just as a simple service taking an explicit type-ID and providing the new object?
|
||||
|
||||
!!chosen design
|
||||
My main goal is to have an easy-to-use interface for the implementer of individual classes using this factory mechanism. The intended use should mimic the standard use of operator new, and at the same time there should be a possibility to configure »real« Factory behaviour in.
|
||||
|
||||
For this reason I make Factory a Functor, so it can be incorporated as a member into another class, while still looking like a function call to the client code. The users of this factory template typically either parametrize it with existing smart-pointer types, or they may chose to create a specialized Factory derived from this Factory template. After typically hiding this configuration behind a typedef, the user adds a static field to the class intended to use the Factory and initializes this field with the concrete Factory (this may pass internal ~IDs to the constructor of the Factory and from there on to the underlying Allocator).
|
||||
{{{
|
||||
#include "common/factory.hpp"
|
||||
|
||||
class Product
|
||||
{
|
||||
int wow_;
|
||||
|
||||
public:
|
||||
typedef lumiera::factory::RefcntFactory<Product> Factory;
|
||||
static Factory create;
|
||||
|
||||
Product() : wow_(42) {} ;
|
||||
};
|
||||
|
||||
/** storage for a static Factory instance
|
||||
* for creating refcounting Ptrs to Product objects
|
||||
*/
|
||||
Product::Factory Product::create; // <----note this is a ctor call
|
||||
}}}
|
||||
Now, the clients of {{{class Product}}} can create ref-counting pointers to Product-objects by doing a fully qualified {{{create()}}} functor call:
|
||||
{{{
|
||||
std::tr1::shared_ptr<Product> huii = Product::create (); // <----will invoke the default Product() ctor
|
||||
std::tr1::shared_ptr<Product> pfuii = huii;
|
||||
}}}
|
||||
Some further details
|
||||
* the product class (or base class) cares for using an custom allocator via an overloaded {{{operator new(size_t)}}} if applicable.
|
||||
* thus, when Functor or any derived class issues a new XX(), our custom Allocator gains control
|
||||
* the Functor-behaviour relies on a custom {{{operator()}}} which can be overridden in derived classes to take various parameter lists.
|
||||
* additionally, such a Factory class derived from Functor can do specific magic and e.g. create some subclass
|
||||
* and, as the created smart-pointer is a template template parameter, such a custom Functor can create all sorts of Proxies, wrappers and the like
|
||||
* a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
|
||||
!flavours
|
||||
* A special kind of factory used at various places is the ''singleton'' factory for access to application wide services and facade interfaces. All singleton factories for the same target type share a single instance of the target, which is created lazily on first usage. Actually, this is our very special version of [[dependency injection|DependencyFactory]]
|
||||
* whenever there is a set of objects of some kind, which require registration and connection to a central entity, client code gets (smart) handles, which are emitted by a factory, which connects to the respective manager for registration automatically.
|
||||
* to bridge the complexities of using an external (plug-in based) interface, proxy objects are created by a factory, wired to invoke the external calls to forward any client invoked operation.
|
||||
* the ''configurable factory'' is used to define a family of production lines, which are addressed by the client by virtue of some type-ID. Runtime data and criteria are used to form this type-ID and thus pick the suitable factory function
|
||||
</pre>
|
||||
</div>
|
||||
<div title="Feed" modifier="Ichthyostega" created="201202122115" modified="201202122123" tags="def Rendering">
|
||||
|
|
@ -2420,7 +2383,7 @@ Finally, this example shows an ''automation'' data set controlling some paramete
|
|||
* from [[play process|PlayProcess]] to [[frame dispatching|FrameDispatcher]] and [[node invocation|NodeInvocation]]
|
||||
</pre>
|
||||
</div>
|
||||
<div title="ImplementationGuidelines" modifier="Ichthyostega" created="200711210531" modified="201111261555" tags="Concepts design discuss">
|
||||
<div title="ImplementationGuidelines" modifier="Ichthyostega" created="200711210531" modified="201310132316" tags="Concepts design discuss" changecount="1">
|
||||
<pre>!Observations, Ideas, Proposals
|
||||
''this page is a scrapbook for collecting ideas'' &mdash; please don't take anything noted here too literal. While writing code, I observe that I (ichthyo) follow certain informal guidelines, some of which I'd like to note down because they could evolve into general style guidelines for the Proc-Layer code.
|
||||
* ''Inversion of Control'' is the leading design principle.
|
||||
|
|
@ -2429,8 +2392,8 @@ Finally, this example shows an ''automation'' data set controlling some paramete
|
|||
* (almost) never {{{delete}}} an object directly, use {{{new}}} only when some smart pointer is at hand.
|
||||
* clearly distinguish ''value objects'' from objects with ''reference semantics'', i.e. objects having a distict //object identity.//
|
||||
* when user/client code is intended to create reference-semantics objects, make the ctor protected and provide a factory member called {{{create}}} instead, returning a smart pointer
|
||||
* similarly, when we need just one instance of a given service, make the ctor protected and provide a factory member called {{{instance}}}, to be implemented by the lumiera::[[Singleton]] factory.
|
||||
* whenever possible, prefer this (lazy initialised [[Singleton]]) approach and avoid static initialisation magic
|
||||
* similarly, when we need just one instance of a given service, make the ctor protected and provide a factory member called {{{instance}}}, to be implemented by the [[dependency factory|DependencyFactory]].
|
||||
* whenever possible, prefer this (lazy initialised [[dependency|DependencyFactory]]) approach and avoid static initialisation magic
|
||||
* avoid doing anything non-local during the startup phase or shutdown phase of the application, especially avoid doing substantial work in any dtor.
|
||||
* avoid asuming anything that can't be enforced by types, interfaces or signatures; this means: be prepared for open possibilities
|
||||
* prefer {{{const}}} and initialisation code over assignment and active changes (inspired by functional programming)
|
||||
|
|
@ -2875,7 +2838,7 @@ Because we deliberately won't make any asumptions about the implementation libra
|
|||
It would be possible to circumvent this problem by requiring all supported implementation libraries to be known at compile time, because then the actual media implementation type could be linked to a facade type by generic programming. Indeed, Lumiera follows this route with regards to the possible kinds of MObject or [[Asset]] &mdash; but to the contraty, for the problem in question here, being able to include support for a new media data type just by adding a plugin by far outweights the benefits of compile-time checked implementation type selection. So, as a consequence of this design decision we //note the possibility of the media file type discovery code to be misconfigured// and select the //wrong implementation library at runtime.// And thus the render engine needs to be prepared for the source reading node of any pipe to flounder completely, and protect the rest of the system accordingly
|
||||
</pre>
|
||||
</div>
|
||||
<div title="MemoryManagement" modifier="Ichthyostega" created="200708100225" modified="201003160208" tags="impl decision rewrite">
|
||||
<div title="MemoryManagement" modifier="Ichthyostega" created="200708100225" modified="201310120030" tags="impl decision rewrite" changecount="2">
|
||||
<pre>Of course: Cinelerra currently leaks memory and crashes regularilly. For the newly written code, besides retaining the same level of performance, a main goal is to use methods and techniques known to support the writing of quality code. So, besides the MultithreadConsiderations, a solid strategy for managing the ownership of allocated memory blocks is necessary right from start.
|
||||
|
||||
!Problems
|
||||
|
|
@ -2885,7 +2848,7 @@ It would be possible to circumvent this problem by requiring all supported imple
|
|||
!C++ solution
|
||||
First of all -- this doesn't concern //every// allocation. It rather means there are certain //dangerous areas// which need to be identified. Anyhow, instead of carrying inherent complexities of the problem into the solution, we should rather look for common solution pattern(s) which help factoring out complexity.
|
||||
|
||||
For the case here in question this seems to be the ''resource allocation is construction'' pattern. Which boils down to basically never using bare pointers when concerned with ownership. Instead, ownership should be handled by smart-pointers.
|
||||
For the case here in question this seems to be the __R__esource __A__llocation __I__s __I__nitialisation pattern (''RAII''). Which boils down to basically never using bare pointers when concerned with ownership. Client code allways gets to use a wrapper object, which cannot be obtained unless going through some well defined construction site. As an extension to the baisc RAII pattern, C++ allows us to build //smart wrapper objects//, thereby delegating any kind of de-registration or resource freeing automatically. This usage pattern doesn't necessarily imply (and in fact isn't limited to) just ref-counting.
|
||||
|
||||
!!usage scenarios
|
||||
# __existence is being used__: Objects just live for being referred to in a object network. In this case, use refcounting smart-pointers for every ref. (note: problem with cyclic refs)
|
||||
|
|
@ -2905,7 +2868,7 @@ For the case here in question this seems to be the ''resource allocation is cons
|
|||
* the [[sequences|Sequence]] and the defined [[assets|Asset]] belong together to one [[Session]]. If the Session is closed, this means a internal shutdown of the whole ProcLayer, i.e. closing of all GUI representations and terminating all render processes. If these calles are implemented as blocking operations, we can assert that as long as any GUI representation or any render process is running, there is a valid session and model.
|
||||
|
||||
!using Factories
|
||||
And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
|
||||
And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Generally speaking, client code shouldn't issue "new" and "delete" when it comes in handy. Questions of setup and lifecycle should allways be delegated, typically through the usage of some [[factory|Factories]], which might return the product conveniently wrapped into a RAII style handle. Memory allocation is crucial for performance, and needs to be adapted to the actual platform -- which is impossible unless abstracted and treated as a separate concern.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="MetaAsset" modifier="Ichthyostega" created="201012290320" modified="201101151026" tags="def">
|
||||
|
|
|
|||
Loading…
Reference in a new issue