Skip to content

Commit

Permalink
Issue 614 Module & ConfiguredModule (#704)
Browse files Browse the repository at this point in the history
# Issue 614 Module & ConfiguredModule

Fixes #614 (partially)

---
# Change Description

## Changes to `Module<>`

* Make `Module` independent of Nextsim. It is now a separate Module base
class that can be used for any C++ project.
* Introduce a `NextsimModule` class that specializes the newly generic
`Module` for nextSIM-DG.
* Replace `#include "Module.hpp"` with `#include "NextsimModule.hpp"`.
* Add a map from interface name to implementation name in the `Module`
namespace alongside the `Module` template class. This string-to-string
map is not available elsewhere. The data is stored as a function-local
`static` variable.
* Remove the class-static instance of the reference implementation from
the `Module<>` template. This static instance is now a function-local
`static` variable, so at least the construction order is well defined.
The lifetime is still until the executable epilogue.
* Delete the module classes. Where there was originally (for example) a
`DiagnosticOutputModule` class that inherited from
`Module<IDiagnosticModule>`, everything now refers to the template
instantiation. The `…Module` class was necessary to run the register the
module at static start-up. With this no longer necessary,
  * the classes are no longer needed
  * the class headers (`…Module.hpp`) have been removed
  * the class header creation script `module_header.py` has been removed
* the command in `CMakeLists.txt` to run the header script has been
removed.
* `#include`s of the `…Module.hpp` file have been replaced with an
`#include` of `NextsimModule.hpp` and an `#include` of the interface
header file.
* Change the API of `Module<>`. It now depends on the definition of two
classes before it is `#include`d:
  * `HelpMap`
* `Config` which defines a `std::string Config::getImpl(const
std::string&)` function.
* There may be a better way to handle this, but more template arguments
was much too complex a solution.
* Add internal options to set the implementation without setting the
unique instance to prevent recursive initialization. The external API
remains the same.
* Move the class-static data in `Module<>` to be function-local statics.
This requires a slight change to the API and a different format in the
generated `module.cpp` files.
* The `fn` type is now a plain function pointer, rather than a
`std::function` object. This required replacing a lambda with an ad-hoc
function in one test.
* The variables holding the implementation names are now namespaced into
`namespace Module::InterfaceClass`.
* The necessary changes to `module_builder.py` to support the above
changes.
* Adds a finalizer method to the `Module<>` template. This will call the
destructor on the static instance, allowing all the data arrays the
static instance holds to be deleted.

## Changes to `ConfiguredModule`
* Remove static data from `ConfiguredModule`. Module configurations are
now parsed on demand.
* Remove the call from `main` to parse the configuration for the
modules.
* With the change to lazy configuration, it is impossible to reconfigure
modules automatically from the config text. Change to explicit calls to
`setImplementation()`.
* The `ConfiguredModule_test` now only checks that the module
configuration options are parsed correctly. Everything else is handeled
by the `Module<>` template class.

## Miscellaneous other changes
* Removed the directory `ModuleLoaderTestModules`. These are very
defunct.
* A fix to `IceGrowth_test`. This is so far from the Module changes, I
don't see how it could have been working before.

---
# Test Description

All unit tests still pass.
  • Loading branch information
timspainNERSC authored Sep 23, 2024
2 parents 8a22ec7 + b505d7d commit c0bc27c
Show file tree
Hide file tree
Showing 50 changed files with 469 additions and 562 deletions.
57 changes: 17 additions & 40 deletions core/src/ConfiguredModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "include/ConfiguredModule.hpp"

#include "include/Configurator.hpp"
#include "include/Module.hpp"
#include "include/NextsimModule.hpp"

#include <boost/program_options.hpp>
#include <stdexcept>
Expand All @@ -17,59 +17,36 @@ namespace Nextsim {

const std::string ConfiguredModule::MODULE_PREFIX = "Modules";

ConfiguredModule::map ConfiguredModule::configuredModules;

void ConfiguredModule::parseConfigurator()
{
// A default string that can never be a valid C++ class name
std::string defaultStr = "+++DEFAULT+++";
// Construct a new options map
boost::program_options::options_description opt;

for (auto entry : configuredModules) {
std::string module = entry.first;
opt.add_options()(addPrefix(module).c_str(),
boost::program_options::value<std::string>()->default_value(defaultStr),
("Load an implementation of " + module).c_str());
}

boost::program_options::variables_map vm = Configurator::parse(opt);

for (auto entry : configuredModules) {
std::string implString = vm[addPrefix(entry.first)].as<std::string>();
// Only do anything if the retrieved option is not the default value
if (implString != defaultStr) {
entry.second.first(implString);
}
}
}
// A default string that can never be a valid C++ class name
static const std::string defaultStr = "+++DEFAULT+++";

std::string ConfiguredModule::addPrefix(const std::string& moduleName)
{
return MODULE_PREFIX + "." + moduleName;
}

void ConfiguredModule::setConfiguredModules(const map& ls)
std::string ConfiguredModule::getImpl(const std::string& module)
{
map newMap(ls);
configuredModules.merge(newMap);
}
boost::program_options::options_description opt;

void ConfiguredModule::configureModule(const std::string& mod, const fn& setter, const ofn& getter)
{
configuredModules[mod] = { setter, getter };
}
opt.add_options()(addPrefix(module).c_str(),
boost::program_options::value<std::string>()->default_value(defaultStr),
("Load an implementation of " + module).c_str());

std::string ConfiguredModule::getModuleConfiguration(const std::string& module)
{
return configuredModules[module].second();
std::string implString = Configurator::parse(opt)[addPrefix(module)].as<std::string>();
// Only do anything if the retrieved option is not the default value
if (implString != defaultStr) {
return implString;
} else {
return "";
}
}

ConfigMap ConfiguredModule::getAllModuleConfigurations()
{
ConfigMap iiMap;
for (auto entry : configuredModules) {
iiMap[addPrefix(entry.first)] = entry.second.second();
for (const auto& moduleImpl : Module::ImplementationNames::getAll()) {
iiMap[moduleImpl.first] = moduleImpl.second;
}
return iiMap;
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/DevStep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#include "include/ConfiguredModule.hpp"
#include "include/IDiagnosticOutput.hpp"
#include "include/Module.hpp"
#include "include/NextsimModule.hpp"
namespace Nextsim {

DevStep::DevStep()
Expand Down
2 changes: 1 addition & 1 deletion core/src/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "include/DevStep.hpp"
#include "include/IDiagnosticOutput.hpp"
#include "include/MissingData.hpp"
#include "include/Module.hpp"
#include "include/NextsimModule.hpp"
#include "include/StructureFactory.hpp"

#include <string>
Expand Down
3 changes: 2 additions & 1 deletion core/src/ModelMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

#include "include/ModelMetadata.hpp"

#include "include/StructureModule.hpp"
#include "include/IStructure.hpp"
#include "include/NextsimModule.hpp"
#include "include/gridNames.hpp"

#ifdef USE_MPI
Expand Down
2 changes: 1 addition & 1 deletion core/src/PrognosticData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "include/PrognosticData.hpp"

#include "include/ModelArrayRef.hpp"
#include "include/Module.hpp"
#include "include/NextsimModule.hpp"
#include "include/gridNames.hpp"

namespace Nextsim {
Expand Down
3 changes: 2 additions & 1 deletion core/src/StructureFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

#include "include/StructureFactory.hpp"

#include "include/StructureModule.hpp"
#include "include/IStructure.hpp"
#include "include/NextsimModule.hpp"

#include "include/RectGridIO.hpp"

Expand Down
29 changes: 2 additions & 27 deletions core/src/include/ConfiguredModule.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,20 @@
namespace Nextsim {

/*!
* @brief A helper class to automatically load modules based on the
* configuration.
* @brief A helper class to load modules based on the configuration.
*/
class ConfiguredModule {
public:
typedef std::function<void(const std::string&)> fn;
typedef std::function<std::string()> ofn;
typedef std::map<const std::string, std::pair<fn, ofn>> map;
ConfiguredModule() = default;
virtual ~ConfiguredModule() = default;

//! Parse the configuration for all of the modules defined in ModuleLoader.
static void parseConfigurator();

/*!
* Sets the names of all the modules to be configured
* @param ls a map of Module names and the locations of implementation
* setter and getter functions.
*/
static void setConfiguredModules(const map& ls);

/*!
* Adds the name of a module to the list to be configured
* @param mod the name of the class to be configured.
* @param setter the void(std::string) function to set the implementation.
* @param getter the std::string() function to get the implementation.
*/
static void configureModule(const std::string& mod, const fn& setter, const ofn& getter);

/*!
* Gets the implementation name for a named module.
* Returns an empty string if there is no such module.
*
* @param interface the name of the module to be checked.
*/
static std::string getModuleConfiguration(const std::string& interface);
static std::string getImpl(const std::string& interface);

/*!
* Returns a map from interface name to implementation name for all modules
Expand All @@ -71,9 +49,6 @@ class ConfiguredModule {

//! The configuration options section name for modules.
static const std::string MODULE_PREFIX;

private:
static map configuredModules;
};

} /* namespace Nextsim */
Expand Down
Loading

0 comments on commit c0bc27c

Please sign in to comment.