int
main(int argc, char *argv[])
{
- ProgramInfo::init(argc, argv);
- try
- {
- AnalysisTemplate module;
- TrajectoryAnalysisCommandLineRunner runner(&module);
- return runner.run(argc, argv);
- }
- catch (const std::exception &ex)
- {
- gmx::printFatalErrorMessage(stderr, ex);
- return 1;
- }
+ return gmx::TrajectoryAnalysisCommandLineRunner::runAsMain<AnalysisTemplate>(argc, argv);
}
* To implement a command-line tool, it should create a module and run it using
* gmx::TrajectoryAnalysisCommandLineRunner using the boilerplate code below:
* \skip int
- * \until return 1;
- * \until }
* \until }
*
*
*
* Owns the contained modules.
*/
- CommandLineModuleMap modules_;
+ CommandLineModuleMap modules_;
//! Information about the currently running program.
- ProgramInfo &programInfo_;
+ ProgramInfo &programInfo_;
/*! \brief
* Module that implements help for the binary.
*
* The pointed module is owned by the \a modules_ container.
*/
- CommandLineHelpModule *helpModule_;
+ CommandLineHelpModule *helpModule_;
//! Settings for what to write in the startup header.
- BinaryInformationSettings binaryInfoSettings_;
+ BinaryInformationSettings binaryInfoSettings_;
+ //! If non-NULL, run this module in single-module mode.
+ CommandLineModuleInterface *singleModule_;
//! Whether all stderr output should be suppressed.
- bool bQuiet_;
+ bool bQuiet_;
//! Whether to write the startup information to stdout iso stderr.
- bool bStdOutInfo_;
+ bool bStdOutInfo_;
private:
GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
};
CommandLineModuleManager::Impl::Impl(ProgramInfo *programInfo)
- : programInfo_(*programInfo), helpModule_(NULL), bQuiet_(false),
- bStdOutInfo_(false)
+ : programInfo_(*programInfo), helpModule_(NULL), singleModule_(NULL),
+ bQuiet_(false), bStdOutInfo_(false)
{
}
CommandLineModuleInterface *
CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
{
+ if (singleModule_ != NULL)
+ {
+ // TODO: Process common options also in this case.
+ return singleModule_;
+ }
// Check if the module is called through a symlink.
CommandLineModuleMap::const_iterator module
= findModuleFromBinaryName(programInfo_);
CommandLineModuleManager::CommandLineModuleManager(ProgramInfo *programInfo)
: impl_(new Impl(programInfo))
{
- impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
- addModule(CommandLineModulePointer(impl_->helpModule_));
}
CommandLineModuleManager::~CommandLineModuleManager()
void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
{
+ if (impl_->helpModule_ == NULL)
+ {
+ impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
+ addModule(CommandLineModulePointer(impl_->helpModule_));
+ }
impl_->helpModule_->addTopic(move(topic));
}
return rc;
}
+// static
+int CommandLineModuleManager::runAsMainSingleModule(
+ int argc, char *argv[], CommandLineModuleInterface *module)
+{
+ ProgramInfo &programInfo = ProgramInfo::init(argc, argv);
+ try
+ {
+ CommandLineModuleManager manager(&programInfo);
+ manager.impl_->singleModule_ = module;
+ return manager.run(argc, argv);
+ }
+ catch (const std::exception &ex)
+ {
+ printFatalErrorMessage(stderr, ex);
+ return 1;
+ }
+}
+
} // namespace gmx
*
* Typical usage:
* \code
- int
- main(int argc, char *argv[])
+ int main(int argc, char *argv[])
{
const gmx::ProgramInfo &programInfo =
gmx::ProgramInfo::init("gmx", argc, argv);
class CommandLineModuleManager
{
public:
+ /*! \brief
+ * Implements a main() method that runs a single module.
+ *
+ * \param argc \c argc passed to main().
+ * \param argv \c argv passed to main().
+ * \param module Module to run.
+ *
+ * This method allows for uniform behavior for binaries that only
+ * contain a single module without duplicating any of the
+ * implementation from CommandLineModuleManager (startup headers,
+ * common options etc.).
+ *
+ * The signature assumes that \p module construction does not throw
+ * (because otherwise the caller would need to duplicate all the
+ * exception handling code). It is possible to move the construction
+ * inside the try/catch in this method using an indirection similar to
+ * TrajectoryAnalysisCommandLineRunner::runAsMain(), but until that is
+ * necessary, the current approach leads to simpler code.
+ *
+ * Usage:
+ * \code
+ int main(int argc, char *argv[])
+ {
+ CustomCommandLineModule module;
+ return gmx::CommandLineModuleManager::runAsMainSingleModule(argc, argv, &module);
+ }
+ * \endcode
+ *
+ * Does not throw. All exceptions are caught and handled internally.
+ */
+ static int runAsMainSingleModule(int argc, char *argv[],
+ CommandLineModuleInterface *module);
+
/*! \brief
* Initializes a command-line module manager.
*
#include "config.h"
#endif
-#include "gromacs/legacyheaders/copyrite.h"
#include "gromacs/legacyheaders/pbc.h"
#include "gromacs/legacyheaders/rmpbc.h"
#include "gromacs/legacyheaders/statutil.h"
#include "gromacs/analysisdata/paralleloptions.h"
#include "gromacs/commandline/cmdlinehelpwriter.h"
+#include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/commandline/cmdlinemodulemanager.h"
#include "gromacs/commandline/cmdlineparser.h"
#include "gromacs/onlinehelp/helpwritercontext.h"
#include "gromacs/options/options.h"
class TrajectoryAnalysisCommandLineRunner::Impl
{
public:
+ class RunnerCommandLineModule;
+
Impl(TrajectoryAnalysisModule *module);
~Impl();
TrajectoryAnalysisModule *module_;
int debugLevel_;
- bool bStandalone_;
};
TrajectoryAnalysisCommandLineRunner::Impl::Impl(
TrajectoryAnalysisModule *module)
- : module_(module), debugLevel_(0), bStandalone_(true)
+ : module_(module), debugLevel_(0)
{
}
}
-void
-TrajectoryAnalysisCommandLineRunner::setStandalone(bool bStandalone)
-{
- impl_->bStandalone_ = bStandalone;
-}
-
-
void
TrajectoryAnalysisCommandLineRunner::setSelectionDebugLevel(int debuglevel)
{
{
TrajectoryAnalysisModule *module = impl_->module_;
- if (impl_->bStandalone_)
- {
- printBinaryInformation(stderr, ProgramInfo::getInstance());
- }
-
SelectionCollection selections;
selections.setDebugLevel(impl_->debugLevel_);
module->finishAnalysis(nframes);
module->writeOutput();
- if (impl_->bStandalone_)
- {
- gmx_thanx(stderr);
- }
-
return 0;
}
.writeHelp(context);
}
+
+/*! \internal \brief
+ * Command line module for a trajectory analysis module.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisCommandLineRunner::Impl::RunnerCommandLineModule
+ : public CommandLineModuleInterface
+{
+ public:
+ /*! \brief
+ * Constructs a module.
+ *
+ * \param[in] name Name for the module.
+ * \param[in] description One-line description for the module.
+ * \param[in] factory Factory method to create the analysis module.
+ *
+ * Does not throw. This is important for correct implementation of
+ * runAsMain().
+ */
+ RunnerCommandLineModule(const char *name, const char *description,
+ ModuleFactoryMethod factory)
+ : name_(name), description_(description), factory_(factory)
+ {
+ }
+
+ virtual const char *name() const { return name_; }
+ virtual const char *shortDescription() const { return description_; };
+
+ virtual int run(int argc, char *argv[]);
+ virtual void writeHelp(const HelpWriterContext &context) const;
+
+ private:
+ const char *name_;
+ const char *description_;
+ ModuleFactoryMethod factory_;
+
+ GMX_DISALLOW_COPY_AND_ASSIGN(RunnerCommandLineModule);
+};
+
+int TrajectoryAnalysisCommandLineRunner::Impl::RunnerCommandLineModule::run(
+ int argc, char *argv[])
+{
+ TrajectoryAnalysisModulePointer module(factory_());
+ TrajectoryAnalysisCommandLineRunner runner(module.get());
+ return runner.run(argc, argv);
+}
+
+void TrajectoryAnalysisCommandLineRunner::Impl::RunnerCommandLineModule::writeHelp(
+ const HelpWriterContext &context) const
+{
+ TrajectoryAnalysisModulePointer module(factory_());
+ TrajectoryAnalysisCommandLineRunner runner(module.get());
+ runner.writeHelp(context);
+}
+
+// static
+int
+TrajectoryAnalysisCommandLineRunner::runAsMain(
+ int argc, char *argv[], ModuleFactoryMethod factory)
+{
+ Impl::RunnerCommandLineModule module(NULL, NULL, factory);
+ return CommandLineModuleManager::runAsMainSingleModule(argc, argv, &module);
+}
+
+// static
+void
+TrajectoryAnalysisCommandLineRunner::registerModule(
+ CommandLineModuleManager *manager, const char *name,
+ const char *description, ModuleFactoryMethod factory)
+{
+ CommandLineModulePointer module(
+ new Impl::RunnerCommandLineModule(name, description, factory));
+ manager->addModule(move(module));
+}
+
} // namespace gmx
#define GMX_TRAJECTORYANALYSIS_CMDLINERUNNER_H
#include "../utility/common.h"
+#include "../utility/uniqueptr.h"
namespace gmx
{
+class CommandLineModuleManager;
class HelpWriterContext;
class TrajectoryAnalysisModule;
class TrajectoryAnalysisCommandLineRunner
{
public:
+ /*! \brief
+ * Implements a main() method that runs a given module.
+ *
+ * \tparam ModuleType Trajectory analysis module.
+ * \param argc \c argc passed to main().
+ * \param argv \c argv passed to main().
+ *
+ * This method abstracts away all the logic required to implement a
+ * main() method in user tools, allowing that to be changed without
+ * requiring changes to the tools themselves.
+ *
+ * \p ModuleType should be default-constructible and derive from
+ * TrajectoryAnalysisModule.
+ *
+ * Does not throw. All exceptions are caught and handled internally.
+ */
+ template <class ModuleType>
+ static int runAsMain(int argc, char *argv[])
+ {
+ return runAsMain(argc, argv, &createModule<ModuleType>);
+ }
+ /*! \brief
+ * Registers a command-line module that runs a given module.
+ *
+ * \tparam ModuleType Trajectory analysis module.
+ * \param manager Manager to register the module to.
+ * \param name Name of the module to register.
+ * \param description One-line description for the module to register.
+ *
+ * \p ModuleType should be default-constructible and derive from
+ * TrajectoryAnalysisModule.
+ *
+ * \p name and \p descriptions must be string constants or otherwise
+ * stay valid for the duration of the program execution.
+ */
+ template <class ModuleType>
+ static void registerModule(CommandLineModuleManager *manager,
+ const char *name, const char *description)
+ {
+ registerModule(manager, name, description, &createModule<ModuleType>);
+ }
+
/*! \brief
* Create a new runner with the provided module.
*
TrajectoryAnalysisCommandLineRunner(TrajectoryAnalysisModule *module);
~TrajectoryAnalysisCommandLineRunner();
- /*! \brief
- * Sets whether the runner will print standalone messages.
- *
- * \param[in] bStandalone Whether the runner is run directly from main().
- *
- * By default, the runner takes responsibility of printing some
- * standard Gromacs messages at startup and at the end.
- * This is used internally when executing the runner in a context where
- * these messages are handled on a higher level.
- *
- * Does not throw.
- */
- void setStandalone(bool bStandalone);
/*! \brief
* Sets the default debugging level for selections.
*
void writeHelp(const HelpWriterContext &context);
private:
+ //! Smart pointer type for managing a trajectory analysis module.
+ typedef gmx_unique_ptr<TrajectoryAnalysisModule>::type
+ TrajectoryAnalysisModulePointer;
+
+ /*! \brief
+ * Factory method type for creating a trajectory analysis module.
+ *
+ * This method allows the module creation to be postponed to be inside
+ * the try/catch block in runAsMain()/registerModule() implementation
+ * methods and still keep the implementation out of the header, making
+ * the ABI more stable.
+ */
+ typedef TrajectoryAnalysisModulePointer (*ModuleFactoryMethod)();
+ /*! \brief
+ * Creates a trajectory analysis module of a given type.
+ *
+ * \tparam ModuleType Module to create.
+ */
+ template <class ModuleType>
+ static TrajectoryAnalysisModulePointer createModule()
+ {
+ return TrajectoryAnalysisModulePointer(new ModuleType());
+ }
+
+ //! Implements the template runAsMain() method.
+ static int runAsMain(int argc, char *argv[],
+ ModuleFactoryMethod factory);
+ //! Implements the template registerModule() method.
+ static void registerModule(CommandLineModuleManager *manager,
+ const char *name, const char *description,
+ ModuleFactoryMethod factory);
+
class Impl;
PrivateImplPointer<Impl> impl_;
*/
#include "gromacs/trajectoryanalysis/modules.h"
-#include "gromacs/commandline/cmdlinemodule.h"
-#include "gromacs/commandline/cmdlinemodulemanager.h"
#include "gromacs/trajectoryanalysis/cmdlinerunner.h"
#include "modules/angle.h"
namespace
{
-/*! \internal \brief
- * Base class for trajectory analysis module command-line wrappers.
+/*! \brief
+ * Convenience method for registering a command-line module for trajectory
+ * analysis.
*
- * This class exists to avoid multiple identical copies of the \p run() method
- * to be generated for the TrajAnalysisCmdLineWrapper template classes.
+ * \tparam ModuleType Trajectory analysis module to wrap.
*
- * \ingroup module_trajectoryanalysis
- */
-class AbstractTrajAnalysisCmdLineWrapper : public CommandLineModuleInterface
-{
- public:
- virtual const char *name() const = 0;
- virtual const char *shortDescription() const = 0;
-
- virtual int run(int argc, char *argv[]);
- virtual void writeHelp(const HelpWriterContext &context) const;
-
- protected:
- //! Creates the analysis module for this wrapper.
- virtual TrajectoryAnalysisModulePointer createModule() const = 0;
-};
-
-int AbstractTrajAnalysisCmdLineWrapper::run(int argc, char *argv[])
-{
- TrajectoryAnalysisModulePointer module(createModule());
- TrajectoryAnalysisCommandLineRunner runner(module.get());
- runner.setStandalone(false);
- return runner.run(argc, argv);
-}
-
-void AbstractTrajAnalysisCmdLineWrapper::writeHelp(const HelpWriterContext &context) const
-{
- TrajectoryAnalysisModulePointer module(createModule());
- TrajectoryAnalysisCommandLineRunner runner(module.get());
- runner.writeHelp(context);
-}
-
-/*! \internal \brief
- * Template for command-line wrapper of a trajectory analysis module.
- *
- * \tparam Module Trajectory analysis module to wrap.
- *
- * \p Module should be default-constructible, derive from
- * TrajectoryAnalysisModule, and have a static public members
+ * \p ModuleType should be default-constructible, derive from
+ * TrajectoryAnalysisModule, and have static public members
* \c "const char name[]" and \c "const char shortDescription[]".
*
* \ingroup module_trajectoryanalysis
*/
-template <class Module>
-class TrajAnalysisCmdLineWrapper : public AbstractTrajAnalysisCmdLineWrapper
+template <class ModuleType>
+void registerModule(CommandLineModuleManager *manager)
{
- public:
- virtual const char *name() const
- {
- return Module::name;
- }
- virtual const char *shortDescription() const
- {
- return Module::shortDescription;
- }
- virtual TrajectoryAnalysisModulePointer createModule() const
- {
- return TrajectoryAnalysisModulePointer(new Module);
- }
-};
+ TrajectoryAnalysisCommandLineRunner::registerModule<ModuleType>(
+ manager, ModuleType::name, ModuleType::shortDescription);
+}
} // namespace
void registerTrajectoryAnalysisModules(CommandLineModuleManager *manager)
{
using namespace gmx::analysismodules;
- manager->registerModule<TrajAnalysisCmdLineWrapper<Angle> >();
- manager->registerModule<TrajAnalysisCmdLineWrapper<Distance> >();
- manager->registerModule<TrajAnalysisCmdLineWrapper<FreeVolume> >();
- manager->registerModule<TrajAnalysisCmdLineWrapper<Select> >();
+ registerModule<Angle>(manager);
+ registerModule<Distance>(manager);
+ registerModule<FreeVolume>(manager);
+ registerModule<Select>(manager);
}
} // namespace gmx
}
TrajectoryAnalysisCommandLineRunner runner(&module);
- runner.setStandalone(false);
int rc = 0;
EXPECT_NO_THROW_GMX(rc = runner.run(impl_->cmdline_.argc(), impl_->cmdline_.argv()));
EXPECT_EQ(0, rc);
}
// static
-const ProgramInfo &ProgramInfo::init(int argc, const char *const argv[])
+ProgramInfo &ProgramInfo::init(int argc, const char *const argv[])
{
return init(NULL, argc, argv);
}
*
* Does not throw. Terminates the program on out-of-memory error.
*/
- static const ProgramInfo &init(int argc, const char *const argv[]);
+ static ProgramInfo &init(int argc, const char *const argv[]);
/*! \brief
* Initializes global program information with explicit binary name.
*