From: Vadim Zeitlin Date: Sun, 11 Jun 2006 22:19:12 +0000 (+0000) Subject: added possibility to specify modules dependencies X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/af266e5bf7e064ba818c1b1619325eb8c5b874f7 added possibility to specify modules dependencies git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@39677 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index 7e446ee585..f53c036692 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -49,6 +49,7 @@ All: - Added wxStandardPaths::GetDocumentsDir() (Ken Thomases) - Added wxStringTokenizer::GetLastDelimiter(); improved documentation. - Fixed wxTextFile in Unicode build +- Added possibility to specify dependencies for a wxModule - Speed improvements to wxRegEx when matching is done in a loop such as during a search and replace. - Fix regerror and regfree name conficts when built-in regex and system regex diff --git a/docs/latex/wx/module.tex b/docs/latex/wx/module.tex index b3f58fec88..795746352d 100644 --- a/docs/latex/wx/module.tex +++ b/docs/latex/wx/module.tex @@ -1,14 +1,16 @@ \section{\class{wxModule}}\label{wxmodule} -The module system is a very simple mechanism to allow applications (and parts of wxWidgets itself) to -define initialization and cleanup functions that are automatically called on wxWidgets -startup and exit. - -To define a new kind of module, derive a class from wxModule, override the OnInit and OnExit functions, -and add the DECLARE\_DYNAMIC\_CLASS and IMPLEMENT\_DYNAMIC\_CLASS to header and implementation files -(which can be the same file). On initialization, wxWidgets will find all classes derived from wxModule, -create an instance of each, and call each OnInit function. On exit, wxWidgets will call the OnExit -function for each module instance. +The module system is a very simple mechanism to allow applications (and parts +of wxWidgets itself) to define initialization and cleanup functions that are +automatically called on wxWidgets startup and exit. + +To define a new kind of module, derive a class from wxModule, override the +\helpref{OnInit}{wxmoduleoninit} and \helpref{OnExit}{wxmoduleonexit} +functions, and add the DECLARE\_DYNAMIC\_CLASS and IMPLEMENT\_DYNAMIC\_CLASS to +header and implementation files (which can be the same file). On +initialization, wxWidgets will find all classes derived from wxModule, create +an instance of each, and call each OnInit function. On exit, wxWidgets will +call the OnExit function for each module instance. Note that your module class does not have to be in a header file. @@ -18,17 +20,31 @@ For example: // A module to allow DDE initialization/cleanup // without calling these functions from app.cpp or from // the user's application. - class wxDDEModule: public wxModule { - DECLARE_DYNAMIC_CLASS(wxDDEModule) public: - wxDDEModule() {} - bool OnInit() { wxDDEInitialize(); return true; }; - void OnExit() { wxDDECleanUp(); }; + wxDDEModule() { } + virtual bool OnInit() { wxDDEInitialize(); return true; }; + virtual void OnExit() { wxDDECleanUp(); }; + + private: + DECLARE_DYNAMIC_CLASS(wxDDEModule) }; IMPLEMENT_DYNAMIC_CLASS(wxDDEModule, wxModule) + + + // Another module which uses DDE in its OnInit() + class MyModule: public wxModule + { + public: + wxDDEModule() { AddDependency(CLASSINFO(wxDDEModule)); } + virtual bool OnInit() { ... code using DDE ... } + virtual void OnExit() { ... } + + private: + DECLARE_DYNAMIC_CLASS(wxDDEModule) + }; \end{verbatim} \wxheading{Derived from} @@ -41,28 +57,47 @@ For example: \latexignore{\rtfignore{\wxheading{Members}}} + \membersection{wxModule::wxModule}\label{wxmodulector} \func{}{wxModule}{\void} Constructs a wxModule object. + \membersection{wxModule::\destruct{wxModule}}\label{wxmoduledtor} \func{}{\destruct{wxModule}}{\void} Destructor. + +\membersection{wxModule::AddDependency}\label{wxmoduleoninit} + +\func{void}{AddDependency}{\param{wxClassInfo * }{dep}} + +Call this function from the constructor of the derived class. \arg{dep} must be +the \helpref{CLASSINFO}{classinfo} of a wxModule-derived class and the +corresponding module will be loaded \emph{before} and unloaded \emph{after} +this module. + +Note that circular dependencies are detected and result in a fatal error. + +\wxheading{Parameters} + +\docparam{dep}{The class information object for the dependent module.} + + \membersection{wxModule::OnExit}\label{wxmoduleonexit} \func{virtual void}{OnExit}{\void} Provide this function with appropriate cleanup for your module. + \membersection{wxModule::OnInit}\label{wxmoduleoninit} \func{virtual bool}{OnInit}{\void} Provide this function with appropriate initialization for your module. If the function returns false, wxWidgets will exit immediately. - diff --git a/include/wx/module.h b/include/wx/module.h index b73a840b46..a35aab0e5e 100644 --- a/include/wx/module.h +++ b/include/wx/module.h @@ -14,11 +14,17 @@ #include "wx/object.h" #include "wx/list.h" +#include "wx/dynarray.h" // declare a linked list of modules class WXDLLIMPEXP_BASE wxModule; WX_DECLARE_USER_EXPORTED_LIST(wxModule, wxModuleList, WXDLLIMPEXP_BASE); +// and an array of class info objects +WX_DEFINE_USER_EXPORTED_ARRAY_PTR(wxClassInfo *, wxArrayClassInfo, + class WXDLLIMPEXP_BASE); + + // declaring a class derived from wxModule will automatically create an // instance of this class on program startup, call its OnInit() method and call // OnExit() on program termination (but only if OnInit() succeeded) @@ -48,7 +54,7 @@ public: static void RegisterModule(wxModule *module); static void RegisterModules(); static bool InitializeModules(); - static void CleanUpModules(); + static void CleanUpModules() { DoCleanUpModules(m_modules); } // used by wxObjectLoader when unloading shared libs's @@ -57,6 +63,40 @@ public: protected: static wxModuleList m_modules; + // the function to call from constructor of a deriving class add module + // dependency which will be initialized before the module and unloaded + // after that + void AddDependency(wxClassInfo *dep) + { + wxCHECK_RET( dep, _T("NULL module dependency") ); + + m_dependencies.Add(dep); + } + +private: + // initialize module and Append it to initializedModules list recursively + // calling itself to satisfy module dependencies if needed + static bool + DoInitializeModule(wxModule *module, wxModuleList &initializedModules); + + // cleanup the modules in the specified list (which may not contain all + // modules if we're called during initialization because not all modules + // could be initialized) and also empty m_modules itself + static void DoCleanUpModules(const wxModuleList& modules); + + + // module dependencies: contains + wxArrayClassInfo m_dependencies; + + // used internally while initiliazing/cleaning up modules + enum + { + State_Registered, // module registered but not initialized yet + State_Initializing, // we're initializing this module but not done yet + State_Initialized // module initialized successfully + } m_state; + + DECLARE_CLASS(wxModule) }; diff --git a/samples/console/console.cpp b/samples/console/console.cpp index 14e781d047..7f0b545c80 100644 --- a/samples/console/console.cpp +++ b/samples/console/console.cpp @@ -65,6 +65,7 @@ #define TEST_LOCALE #define TEST_LOG #define TEST_MIME + #define TEST_MODULE #define TEST_PATHLIST #define TEST_ODBC #define TEST_PRINTF @@ -85,7 +86,7 @@ #define TEST_WCHAR #define TEST_ZIP #else // #if TEST_ALL - #define TEST_STDPATHS + #define TEST_MODULE #endif // some tests are interactive, define this to run them @@ -1381,6 +1382,80 @@ static void TestMimeAssociate() #endif // TEST_MIME +// ---------------------------------------------------------------------------- +// module dependencies feature +// ---------------------------------------------------------------------------- + +#ifdef TEST_MODULE + +#include "wx/module.h" + +class wxTestModule : public wxModule +{ +protected: + virtual bool OnInit() { wxPrintf(_T("Load module: %s\n"), GetClassInfo()->GetClassName()); return true; } + virtual void OnExit() { wxPrintf(_T("Unload module: %s\n"), GetClassInfo()->GetClassName()); } +}; + +class wxTestModuleA : public wxTestModule +{ +public: + wxTestModuleA(); +private: + DECLARE_DYNAMIC_CLASS(wxTestModuleA) +}; + +class wxTestModuleB : public wxTestModule +{ +public: + wxTestModuleB(); +private: + DECLARE_DYNAMIC_CLASS(wxTestModuleB) +}; + +class wxTestModuleC : public wxTestModule +{ +public: + wxTestModuleC(); +private: + DECLARE_DYNAMIC_CLASS(wxTestModuleC) +}; + +class wxTestModuleD : public wxTestModule +{ +public: + wxTestModuleD(); +private: + DECLARE_DYNAMIC_CLASS(wxTestModuleD) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxTestModuleC, wxModule) +wxTestModuleC::wxTestModuleC() +{ + AddDependency(CLASSINFO(wxTestModuleD)); +} + +IMPLEMENT_DYNAMIC_CLASS(wxTestModuleA, wxModule) +wxTestModuleA::wxTestModuleA() +{ + AddDependency(CLASSINFO(wxTestModuleB)); + AddDependency(CLASSINFO(wxTestModuleD)); +} + +IMPLEMENT_DYNAMIC_CLASS(wxTestModuleD, wxModule) +wxTestModuleD::wxTestModuleD() +{ +} + +IMPLEMENT_DYNAMIC_CLASS(wxTestModuleB, wxModule) +wxTestModuleB::wxTestModuleB() +{ + AddDependency(CLASSINFO(wxTestModuleD)); + AddDependency(CLASSINFO(wxTestModuleC)); +} + +#endif // TEST_MODULE + // ---------------------------------------------------------------------------- // misc information functions // ---------------------------------------------------------------------------- diff --git a/src/common/module.cpp b/src/common/module.cpp index 961d478d93..9167848b89 100644 --- a/src/common/module.cpp +++ b/src/common/module.cpp @@ -36,6 +36,7 @@ wxModuleList wxModule::m_modules; void wxModule::RegisterModule(wxModule* module) { + module->m_state = State_Registered; m_modules.Append(module); } @@ -69,42 +70,133 @@ void wxModule::RegisterModules() } } -bool wxModule::InitializeModules() +bool wxModule::DoInitializeModule(wxModule *module, + wxModuleList &initializedModules) { - // Initialize user-defined modules - wxModuleList::compatibility_iterator node; - for ( node = m_modules.GetFirst(); node; node = node->GetNext() ) + if ( module->m_state == State_Initializing ) { - wxModule *module = node->GetData(); - if ( !module->Init() ) + wxLogError(_("Circular dependency involving module \"%s\" detected."), + module->GetClassInfo()->GetClassName()); + return false; + } + + module->m_state = State_Initializing; + + const wxArrayClassInfo& dependencies = module->m_dependencies; + + // satisfy module dependencies by loading them before the current module + for ( unsigned int i = 0; i < dependencies.size(); ++i ) + { + wxClassInfo * cinfo = dependencies[i]; + + // Check if the module is already initialized + wxModuleList::compatibility_iterator node; + for ( node = initializedModules.GetFirst(); node; node = node->GetNext() ) { - wxLogError(_("Module \"%s\" initialization failed"), - module->GetClassInfo()->GetClassName()); + if ( node->GetData()->GetClassInfo() == cinfo ) + break; + } + + if ( node ) + { + // this dependency is already initialized, nothing to do + continue; + } - // clean up already initialized modules - process in reverse order - wxModuleList::compatibility_iterator n; - for ( n = node->GetPrevious(); n; n = n->GetPrevious() ) + // find the module in the registered modules list + for ( node = m_modules.GetFirst(); node; node = node->GetNext() ) + { + wxModule *moduleDep = node->GetData(); + if ( moduleDep->GetClassInfo() == cinfo ) { - n->GetData()->OnExit(); + if ( !DoInitializeModule(moduleDep, initializedModules ) ) + { + // failed to initialize a dependency, so fail this one too + return false; + } + + break; } + } + if ( !node ) + { + wxLogError(_("Dependency \"%s\" of module \"%s\" doesn't exist."), + cinfo->GetClassName(), + module->GetClassInfo()->GetClassName()); return false; } } + if ( !module->Init() ) + { + wxLogError(_("Module \"%s\" initialization failed"), + module->GetClassInfo()->GetClassName()); + return false; + } + + wxLogTrace(TRACE_MODULE, wxT("Module \"%s\" initialized"), + module->GetClassInfo()->GetClassName()); + + module->m_state = State_Initialized; + initializedModules.Append(module); + return true; } -void wxModule::CleanUpModules() +// Initialize user-defined modules +bool wxModule::InitializeModules() { - // Cleanup user-defined modules - wxModuleList::compatibility_iterator node; - for ( node = m_modules.GetFirst(); node; node = node->GetNext() ) + wxModuleList initializedModules; + + for ( wxModuleList::compatibility_iterator node = m_modules.GetFirst(); + node; + node = node->GetNext() ) + { + wxModule *module = node->GetData(); + + // the module could have been already initialized as dependency of + // another one + if ( module->m_state == State_Registered ) + { + if ( !DoInitializeModule( module, initializedModules ) ) + { + // failed to initialize all modules, so clean up the already + // initialized ones + DoCleanUpModules(initializedModules); + + return false; + } + } + } + + // remember the real initialisation order + m_modules = initializedModules; + + return true; +} + +// Clean up all currently initialized modules +void wxModule::DoCleanUpModules(const wxModuleList& modules) +{ + // cleanup user-defined modules in the reverse order compared to their + // initialization -- this ensures that dependencies are respected + for ( wxModuleList::compatibility_iterator node = modules.GetLast(); + node; + node = node->GetPrevious() ) { wxLogTrace(TRACE_MODULE, wxT("Cleanup module %s"), node->GetData()->GetClassInfo()->GetClassName()); - node->GetData()->Exit(); + + wxModule * module = node->GetData(); + + wxASSERT_MSG( module->m_state == State_Initialized, + _T("not initialized module being cleaned up") ); + + module->Exit(); + module->m_state = State_Registered; } + // clear all modules, even the non-initialized ones WX_CLEAR_LIST(wxModuleList, m_modules); }