lumiera_documentation/src/lib/element-tracker.hpp
Ichthyostega 250bfeaee5 use the new OptionalRef to protect session shutdown
The original version was sensible to static initialisation order,
which shold be resolved now, because of OptionalLink's dtor
explicitly disabling any further access.
2010-11-06 22:56:17 +01:00

242 lines
7.4 KiB
C++

/*
ELEMENT-TRACKER.hpp - registry for tracking instances automatically
Copyright (C) Lumiera.org
2010, 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 element-tracker.hpp
** Tracking instances automatically on creation and disposal.
** These helper templates can be used to provide a registry of all objects
** of some kind or type. The class to be used with instance tracking needs
** to cooperate, typically by inheriting from lib::AutoRegistered. Moreover,
** the instance lifecycle handling is assumed to utilise lumiera::P or a
** similar smart-ptr, which allows the registry to hold a safe reference.
** Thus, references won't be cleaned up automatically (as there is always
** at least one reference left) -- rather we rely on invoking a dispose or
** unlink function explicitly.
**
** The standard use case for this feature is the management of the structural
** facade assets Timeline and Sequence within the session. They're created
** automatically to reflect some internal structures within the model;
** with the help of lib::ElementTracker we can expose a list of
** all instances as part of the public Session interface.
**
** @warning AutoRegistered does not check accessibility of the configured
** registry, when detaching an element. Especially, when detaching in turn
** gets automatically invoked from some kind of cleanup, care has to be taken
** to ensure the registry is still available. The Functions
** AutoRegistered::is_attached_to and AutoRegistered::deactivateRegistryLink
** can be used for detaching an Registry explicitly before destruction.
** Ignoring this might lead to segfault on application shutdown. /////////////////TICKET #574
**
** @see session-element-tracker-test.cpp
** @see session-interface-modules.hpp
** @see Timeline
** @see Sequence
**
*/
#ifndef LIB_ELEMENT_TRACKER_H
#define LIB_ELEMENT_TRACKER_H
#include "lib/p.hpp"
#include "lib/optional-ref.hpp"
#include "lib/util-foreach.hpp"
#include "lib/ref-array-impl.hpp"
namespace lib {
using lumiera::P;
using util::isSameObject;
/**
* Registry for tracking object instances.
* Custom implementation of the RefArray interface,
* based on a vector. Especially used by the Session
* to keep track of all timelines and sequences.
* Typically, ELM will inherit from AutoRegistered<ELM>,
* which in turn will invoke the registration/deregistration.
* Because of the smart-ptr-from-this problem, removal takes
* a direct reference, as opposed to a smart-ptr.
* @note ELM is required to provide an equality test.
* Depending on the semantics of this equality,
* registration might behave surprisingly, as
* previously registered \em equivalent instances
* will be deregistered prior to appending the
* new instance.
*/
template<typename ELM>
class ElementTracker
: public lib::RefArrayVector<P<ELM> >
{
typedef std::vector<P<ELM> > _Vec;
typedef typename _Vec::iterator Iter;
typedef typename _Vec::const_iterator CIter;
public:
~ElementTracker()
{
try { clear(); }
catch(...) {/*ignored*/}
}
void
clear ()
{
_Vec toKill;
toKill.reserve(_Vec::size());
toKill.swap(*this); // prevent quadratic detach()
ASSERT (0 == _Vec::size());
util::for_each (toKill, unlink_it);
}
void
append (P<ELM> const& asset)
{
REQUIRE (asset, "Attempt to track a NIL element");
remove (*asset);
push_back (asset);
}
void
remove (ELM const& asset)
{
for (Iter i = _Vec::begin();
i != _Vec::end() ; ++i )
if (asset == **i) // _Vec contains smart-ptrs
{ // ELM is required to define '=='
erase (i);
return;
}
}
bool
isRegistered (ELM const& asset) const
{
for (CIter i = _Vec::begin();
i != _Vec::end() ; ++i )
if (asset == **i)
return true;
return false;
}
private:
static void
unlink_it (P<ELM>& elm)
{
REQUIRE (elm);
try { elm->detach(); }
catch(...)
{
WARN (common,"problems while clearing ElementTracker, ignored.");
}
}
};
/**
* Helper mixin template for implementing a type
* intended to participate in automatic element tracking.
* - the element-tracking registry is accessed through
* the static functor #getRegistry
* - a factory and a #detach operation is provided,
* automatically handling registration.
* It is not mandatory to use this template, but
* types participating in automatic element tracking
* should provide equivalent functionality.
*/
template<typename TAR>
class AutoRegistered
{
public:
typedef lib::ElementTracker<TAR> Registry;
typedef lib::OptionalRef<Registry> RegistryLink;
/** detach this element
* from the element-tracking registry.
* @note called when destroying an non-empty registry.
*/
void
detach()
{
if (!getRegistry) return;
TAR& element = static_cast<TAR&> (*this);
getRegistry().remove(element);
ENSURE (!getRegistry().isRegistered(element));
}
typedef lumiera::P<TAR> PTarget;
/** factory for creating smart-ptr managed
* TAR instances, automatically registered
* with the element-tracking registry.
*/
static PTarget
create()
{
REQUIRE (getRegistry);
PTarget newElement (new TAR());
getRegistry().append (newElement);
ENSURE (newElement);
ENSURE (getRegistry().isRegistered(*newElement));
return newElement;
}
static void
setRegistryInstance (Registry& registry_to_use)
{
getRegistry.link_to (registry_to_use);
}
static void
deactivateRegistryLink()
{
getRegistry.clear();
}
static bool
is_attached_to (Registry const& someRegistry)
{
return getRegistry.points_to (someRegistry);
}
protected:
static RegistryLink getRegistry;
};
/** storage for the functor to link an AutoRegistered entity
* to the corresponding registration service */
template<typename TAR>
typename AutoRegistered<TAR>::RegistryLink AutoRegistered<TAR>::getRegistry;
} // namespace lib
#endif