extend Appconfig class to provide Lifecycle callback hooks
esp. for automatic triggering the initialisation of very basic struff (like NoBug...)
This commit is contained in:
parent
ce72947d0c
commit
85189d3f4c
9 changed files with 347 additions and 35 deletions
20
doc/devel/uml/class141957.html
Normal file
20
doc/devel/uml/class141957.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Documentation produced by the Html generator of Bouml (http://bouml.free.fr) -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title>Class ProcDispatcher</title>
|
||||
<link rel="stylesheet" href="style.css" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body bgcolor="#ffffff">
|
||||
|
||||
<div class = "title">Class ProcDispatcher</div>
|
||||
<p></p>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
|
||||
<a name="refclass141957"></a>
|
||||
<p>Declaration :</p><ul><li>C++ : class ProcDispatcher </li></ul><p>Component(s) : <a href="index.html#refcomponent128773"><b>Dispatcher</b></a></p></body>
|
||||
</html>
|
||||
|
|
@ -33,29 +33,37 @@
|
|||
|
||||
|
||||
using util::isnil;
|
||||
using util::cStr;
|
||||
|
||||
namespace lumiera
|
||||
{
|
||||
|
||||
|
||||
#ifndef LUMIERA_VERSION
|
||||
#define LUMIERA_VERSION 3++devel
|
||||
#define LUMIERA_VERSION 0++devel
|
||||
#endif
|
||||
|
||||
|
||||
/** perform initialization on first access.
|
||||
* A call is placed in static initialization code
|
||||
* included in lumiera.h; thus it will happen
|
||||
* probably very early.
|
||||
/** perform initialization triggered on first access.
|
||||
* Will execute the ON_BASIC_INIT hook, but under typical
|
||||
* circumstances this is a NOP, because when callbacks are
|
||||
* added to this hook, the Appconfig singleton instance has
|
||||
* already been created. For this reason, there is special
|
||||
* treatment for the ON_BASIC_INIT in LifecycleHook::add,
|
||||
* causing the provided callbacks being fired immediately.
|
||||
* (btw, this is nothing to be worried of, for the client
|
||||
* code it just behaves like intended).
|
||||
*/
|
||||
Appconfig::Appconfig()
|
||||
: configParam_ (new Configmap)
|
||||
: configParam_ (new Configmap),
|
||||
lifecycleHooks_(new LifecycleRegistry)
|
||||
{
|
||||
//////////
|
||||
NOBUG_INIT;
|
||||
//////////
|
||||
|
||||
INFO(config, "Basic application configuration triggered.");
|
||||
lifecycleHooks_->execute (ON_BASIC_INIT); // note in most cases a NOP
|
||||
|
||||
// install our own handler for undeclared exceptions
|
||||
std::set_unexpected (lumiera::error::lumiera_unexpectedException);
|
||||
|
|
@ -67,11 +75,8 @@ namespace lumiera
|
|||
|
||||
|
||||
|
||||
/** access the configuation value for a given key.
|
||||
* @return empty string for unknown keys, else the corresponding configuration value
|
||||
*/
|
||||
const string &
|
||||
Appconfig::get (const string & key) throw()
|
||||
Appconfig::get (const string & key)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -82,10 +87,64 @@ namespace lumiera
|
|||
catch (...)
|
||||
{
|
||||
ERROR(config, "error while accessing configuration parameter \"%s\".", key.c_str());
|
||||
throw lumiera::error::Fatal ();
|
||||
static string NOTFOUND ("");
|
||||
return NOTFOUND;
|
||||
} }
|
||||
|
||||
|
||||
void
|
||||
Appconfig::lifecycle (Symbol event_label)
|
||||
{
|
||||
instance().lifecycleHooks_->execute(event_label);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ==== implementation LifecycleHook class =======
|
||||
|
||||
typedef LifecycleRegistry::Hook Callback;
|
||||
|
||||
|
||||
LifecycleHook::LifecycleHook (Symbol eventLabel, Callback callbackFun)
|
||||
{
|
||||
this->add (eventLabel,callbackFun);
|
||||
}
|
||||
|
||||
LifecycleHook&
|
||||
LifecycleHook::add (Symbol eventLabel, Callback callbackFun)
|
||||
{
|
||||
Appconfig::instance().lifecycleHooks_->enroll (eventLabel,callbackFun);
|
||||
|
||||
if (!strcmp(ON_BASIC_INIT, eventLabel))
|
||||
callbackFun(); // when this code executes,
|
||||
// then per definition we are already post "basic init"
|
||||
// (which happens in the Appconfig ctor); thus fire it immediately
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace lumiera
|
||||
|
||||
// ==== implementation C interface =======
|
||||
|
||||
void
|
||||
lumiera_LifecycleHook_add (const char* eventLabel, void callbackFun(void))
|
||||
{
|
||||
lumiera::LifecycleHook (eventLabel, callbackFun);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lumiera_Lifecycle_execute (const char* eventLabel)
|
||||
{
|
||||
lumiera::Appconfig::lifecycle (eventLabel);
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
lumiera_Appconfig_get (const char* key)
|
||||
{
|
||||
return cStr (lumiera::Appconfig::get(key));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,18 @@
|
|||
*/
|
||||
|
||||
/** @file appconfig.hpp
|
||||
** This header is special, as it causes global system initialisation
|
||||
** to happen. On inclusion, it places static initialisation code,
|
||||
** which on first run will create the Appconfig singleton instance.
|
||||
** Additionally, the inclusion, configuration and initialisation
|
||||
** of the NoBug library is handled here. Global <i>definitions</i>
|
||||
** for NoBug are placed into the corresponding translation unit
|
||||
** appconfig.cpp"
|
||||
** Registering and managing some application-global services.
|
||||
** Besides \link Appconfig::get querying \endlink for some
|
||||
** "Application property" constants, there is a mechanism for
|
||||
** registering and firing off application lifecycle event hooks.
|
||||
** The implementation of some subsystem can define a static instance
|
||||
** variable of class LifecycleHook, which will place the provided
|
||||
** callback function into a central registry accessable through
|
||||
** the Appconfig singleton instance.
|
||||
**
|
||||
** @see nobugcfg.h
|
||||
** @see lumiera.cpp
|
||||
** @see nobugcfg.cpp
|
||||
** @see sessmanagerimpl.cpp
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -40,6 +43,8 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include "common/lifecycleregistry.hpp"
|
||||
|
||||
#include "nobugcfg.h"
|
||||
|
||||
|
|
@ -49,6 +54,7 @@ namespace lumiera
|
|||
{
|
||||
using std::string;
|
||||
using boost::scoped_ptr;
|
||||
using boost::noncopyable;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -59,14 +65,14 @@ namespace lumiera
|
|||
* @warning don't use Appconfig in destuctors.
|
||||
*/
|
||||
class Appconfig
|
||||
: private noncopyable
|
||||
{
|
||||
private:
|
||||
/** perform initialization on first access.
|
||||
* @see #instance() for Lifecycle */
|
||||
* @see #instance() */
|
||||
Appconfig ();
|
||||
|
||||
Appconfig (const Appconfig&); ///< copy prohibited, not implemented
|
||||
~Appconfig () throw() {}; ///< deletion prohibited
|
||||
~Appconfig () throw() {}; ///< deletion prohibited
|
||||
friend void boost::checked_delete<Appconfig>(Appconfig*);
|
||||
|
||||
|
||||
|
|
@ -85,24 +91,70 @@ namespace lumiera
|
|||
|
||||
/** access the configuation value for a given key.
|
||||
* @return empty string for unknown keys, config value else
|
||||
* @todo do we need such a facility?
|
||||
*/
|
||||
static const string & get (const string& key) throw();
|
||||
static const string & get (const string& key); // never throws
|
||||
|
||||
/** fire off all lifecycle callbacks
|
||||
* registered under the given label */
|
||||
static void lifecycle (Symbol eventLabel);
|
||||
|
||||
// note: if necessary, we can add support
|
||||
// for querying the current lifecycle phase...
|
||||
|
||||
private:
|
||||
typedef std::map<string,string> Configmap;
|
||||
typedef std::auto_ptr<Configmap> PConfig;
|
||||
typedef scoped_ptr<Configmap> PConfig;
|
||||
typedef scoped_ptr<LifecycleRegistry> PLife;
|
||||
|
||||
/** @todo <b>the following is just placeholder code!</b>
|
||||
* Appconfig <i>could</i> do such things if necessary,
|
||||
* or provide similar "allways available" services.
|
||||
*/
|
||||
PConfig configParam_;
|
||||
PLife lifecycleHooks_;
|
||||
|
||||
friend class LifecycleHook;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Symbol ON_BASIC_INIT ("ON_BASIC_INIT"); ///< automatic static init. treated specially
|
||||
Symbol ON_GLOBAL_INIT ("ON_GLOBAL_INIT"); ///< to be triggered in main() @note no magic!
|
||||
Symbol ON_GLOBAL_SHUTDOWN ("ON_GLOBAL_SHUTDOWN"); ///< to be triggered at the end of main() @note no magic!
|
||||
|
||||
// client code is free to register and use additional lifecycle events
|
||||
|
||||
|
||||
/**
|
||||
* define and register a callback for some lifecycle event.
|
||||
* The purpose of this class is to be defined as a static variable
|
||||
* in the implementation of some subsystem (i.e. in the cpp file),
|
||||
* providing the ctor with the pointer to a callback function.
|
||||
* Thus the callback gets enrolled when the corresponding object
|
||||
* file is loaded. The event ON_BASIC_INIT is handled specifically,
|
||||
* firing off the referred callback function as soon as possible.
|
||||
* All other lables are just arbitrary (string) constants and it
|
||||
* is necessary that "someone" cares to fire off the lifcycle events
|
||||
* at the right place. For example, lumiera-main (and the test runner)
|
||||
* calls \c Appconfig::instance().execute(ON_GLOBAL_INIT) (and..SHUTDOWN)
|
||||
*/
|
||||
class LifecycleHook
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
LifecycleHook (Symbol eventLabel, LifecycleRegistry::Hook callbackFun);
|
||||
|
||||
LifecycleHook& add (Symbol eventLabel, LifecycleRegistry::Hook callbackFun); ///< for chained calls (add multiple callbacks)
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace lumiera
|
||||
|
||||
|
||||
extern "C" { //TODO provide a separate header if some C code happens to need this...
|
||||
|
||||
void lumiera_LifecycleHook_add (const char* eventLabel, void callbackFun(void));
|
||||
void lumiera_Lifecycle_execute (const char* eventLabel);
|
||||
const char* lumiera_Appconfig_get (const char* key);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
101
src/common/lifecycleregistry.hpp
Normal file
101
src/common/lifecycleregistry.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
LIFECYCLEREGISTRY.hpp - registry for application lifecycle callbacks
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Christian Thaeter <ct@pipapo.org>
|
||||
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 lifecycleregistry.hpp
|
||||
** Helper for registering lifecycle event callbacks, which are
|
||||
** provided as a global service by lumiera::Appconfig. This service
|
||||
** allows to enroll functions under a given label and then to call
|
||||
** all those registered functions.
|
||||
** @note this is in fact an event mechanism, and if we start using
|
||||
** more than just this basic functionallity, we should switch to
|
||||
** boost::signals. (which has the downside of being an binary
|
||||
** dependency).
|
||||
**
|
||||
** @see appconfig.hpp
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LUMIERA_LIFECYCLE_H
|
||||
#define LUMIERA_LIFECYCLE_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
//#include <string>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
|
||||
namespace lumiera
|
||||
{
|
||||
// using std::string;
|
||||
// using boost::scoped_ptr;
|
||||
using boost::noncopyable;
|
||||
using boost::function;
|
||||
|
||||
typedef const char * const Symbol;
|
||||
|
||||
|
||||
/**
|
||||
* Registry of callback functions accessable by a label (ID)
|
||||
* provided at registration. Registered functions will be added
|
||||
* to a list, which can be triggered via label. Used by Appconfig
|
||||
* to implement the lumiera lifecycle (init, shutdown) hooks.
|
||||
*/
|
||||
class LifecycleRegistry
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void(void)> Hook;
|
||||
typedef std::vector<Hook> Callbacks;
|
||||
typedef Callbacks::iterator Iter;
|
||||
|
||||
|
||||
void enroll (Symbol label, Hook& toCall)
|
||||
{
|
||||
table_[label].push_back(toCall);
|
||||
}
|
||||
|
||||
void execute (Symbol label)
|
||||
{
|
||||
Callbacks& cbs (table_[label]);
|
||||
Iter e = cbs.end();
|
||||
for (Iter p = cbs.begin();
|
||||
p != e; ++p)
|
||||
(*p)(); // invoke callback
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::map<Symbol, Callbacks> table_;
|
||||
|
||||
LifecycleRegistry () {}
|
||||
friend class Appconfig;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace lumiera
|
||||
#endif
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
#include "proc/mobject/session/defsmanager.hpp"
|
||||
#include "common/singleton.hpp"
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <tr1/memory>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,11 @@ out: dtor ~TargetObj(12) successfull
|
|||
END
|
||||
|
||||
|
||||
TEST "LifeCycle_test" LifeCycle_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "RemoveFromSet_test" RemoveFromSet_test <<END
|
||||
out: removed nothing ---> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ]
|
||||
out: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Appconfig(Test) - accessing the allwasy-available Appconfig singleton
|
||||
Appconfig(Test) - accessing the allways-available Appconfig singleton
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
|
@ -28,9 +28,6 @@
|
|||
#include "common/util.hpp"
|
||||
|
||||
|
||||
#include <iostream>
|
||||
using std::cout;
|
||||
|
||||
|
||||
|
||||
namespace lumiera
|
||||
|
|
|
|||
78
tests/common/lifecycletest.cpp
Normal file
78
tests/common/lifecycletest.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
LifeCycle(Test) - checking the lifecycle callback hooks provided by Appconfig
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, 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/appconfig.hpp"
|
||||
|
||||
#include "common/test/run.hpp"
|
||||
#include "common/util.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
namespace lumiera
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
|
||||
bool basicInit (false);
|
||||
bool customCallback (false);
|
||||
|
||||
void basicInitHook () { basicInit = true; }
|
||||
void myCallback() { customCallback = true; }
|
||||
|
||||
Symbol MY_MAGIC_MEGA_EVENT = "dial M for murder";
|
||||
|
||||
|
||||
namespace // register them to be invoced by lifecycle event id
|
||||
{
|
||||
LifecycleHook _schedule1 (ON_BASIC_INIT, &basicInitHook);
|
||||
LifecycleHook _schedule2 (MY_MAGIC_MEGA_EVENT, &myCallback);
|
||||
}
|
||||
|
||||
|
||||
/** @test the global lifecycle hooks got registered,
|
||||
* the ON_BASIC_INIT hook has been already called,
|
||||
* while our custom callback can be trigged at our will
|
||||
*/
|
||||
class LifeCycle_test : public Test
|
||||
{
|
||||
virtual void
|
||||
run (Arg arg)
|
||||
{
|
||||
ASSERT (basicInit, "the basic-init callback hasn't been invoked automatically");
|
||||
|
||||
ASSERT (!customCallback);
|
||||
Appconfig::lifecycle (MY_MAGIC_MEGA_EVENT);
|
||||
ASSERT ( customCallback);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LAUNCHER (LifeCycle_test, "function common");
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace util
|
||||
|
||||
Loading…
Reference in a new issue