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:
Fischlurch 2008-04-13 23:54:39 +02:00
parent ce72947d0c
commit 85189d3f4c
9 changed files with 347 additions and 35 deletions

View 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>

View file

@ -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));
}

View file

@ -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

View 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

View file

@ -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>

View file

@ -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>

View file

@ -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, ]

View file

@ -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

View 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