From af266e5bf7e064ba818c1b1619325eb8c5b874f7 Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Sun, 11 Jun 2006 22:19:12 +0000
Subject: [PATCH] added possibility to specify modules dependencies

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@39677 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt            |   1 +
 docs/latex/wx/module.tex    |  65 ++++++++++++++-----
 include/wx/module.h         |  42 +++++++++++-
 samples/console/console.cpp |  77 +++++++++++++++++++++-
 src/common/module.cpp       | 126 +++++++++++++++++++++++++++++++-----
 5 files changed, 277 insertions(+), 34 deletions(-)

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

