Cmdline: use exceptions for control flow
This is generally frowned upon but it makes this code 10x cleaner. We handle commandLine usage msg printing by using exceptions for control flow. This allows us to centralize the logic for killing the Mind threads in one place. At least with respect to printing the usage msg.
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
// Define a class to hold the options and parse arguments
|
// Define a class to hold the options and parse arguments
|
||||||
class OptionParser
|
class OptionParser
|
||||||
@@ -23,6 +25,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
class Exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Exception() = default;
|
||||||
|
~Exception() override = default;
|
||||||
|
const char* what() const noexcept override = 0;
|
||||||
|
};
|
||||||
|
|
||||||
std::string argv0;
|
std::string argv0;
|
||||||
std::vector<std::string> senseApiLibPath;
|
std::vector<std::string> senseApiLibPath;
|
||||||
std::vector<std::string> senseApiLibs;
|
std::vector<std::string> senseApiLibs;
|
||||||
@@ -33,4 +43,25 @@ public:
|
|||||||
static struct option longOptions[];
|
static struct option longOptions[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OptionsParserError
|
||||||
|
: public std::invalid_argument, public OptionParser::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OptionsParserError(
|
||||||
|
const std::string& errorMessage, const OptionParser& parser);
|
||||||
|
|
||||||
|
const char* what() const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JustPrintUsageNoError
|
||||||
|
: public OptionParser::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit JustPrintUsageNoError(const OptionParser& parser);
|
||||||
|
const char* what() const noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // OPTS_H
|
#endif // OPTS_H
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <marionette/marionette.h>
|
#include <marionette/marionette.h>
|
||||||
#include <salmanoff.h>
|
#include <salmanoff.h>
|
||||||
#include <boost/asio/signal_set.hpp>
|
#include <boost/asio/signal_set.hpp>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
namespace smo {
|
namespace smo {
|
||||||
|
|
||||||
@@ -55,34 +56,15 @@ void ComponentThread::marionetteMain(ComponentThread& self)
|
|||||||
std::cout << __func__ << ": " << PACKAGE_NAME << " " << PACKAGE_VERSION
|
std::cout << __func__ << ": " << PACKAGE_NAME << " " << PACKAGE_VERSION
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
try {
|
options.parseArguments(
|
||||||
options.parseArguments(
|
crtCommandLineArgs.argc, crtCommandLineArgs.argv,
|
||||||
crtCommandLineArgs.argc, crtCommandLineArgs.argv,
|
crtCommandLineArgs.envp);
|
||||||
crtCommandLineArgs.envp);
|
|
||||||
|
|
||||||
std::cout << __func__ << ": " << options.stringifyOptions()
|
std::cout << __func__ << ": " << options.stringifyOptions()
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
|
||||||
catch (const std::invalid_argument& e)
|
|
||||||
{
|
|
||||||
std::cerr << __func__ << ": Exception occurred: " << e.what()
|
|
||||||
<< '\n';
|
|
||||||
|
|
||||||
options.printUsage = true;
|
if (options.printUsage) {
|
||||||
mrntt::exitCode = EXIT_FAILURE;
|
throw JustPrintUsageNoError(options);
|
||||||
}
|
|
||||||
|
|
||||||
if (options.printUsage)
|
|
||||||
{
|
|
||||||
/* We could make RAII guard classes to always shutdown the mind
|
|
||||||
* threads properly in these pre-event loop situations.
|
|
||||||
*/
|
|
||||||
mind.finalizeReq([]{
|
|
||||||
mrntt::mrntt->getIoService().stop();
|
|
||||||
});
|
|
||||||
self.getIoService().reset();
|
|
||||||
self.getIoService().run();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.getIoService().post([]()
|
self.getIoService().post([]()
|
||||||
@@ -130,6 +112,25 @@ void ComponentThread::marionetteMain(ComponentThread& self)
|
|||||||
std::cout << __func__ << ": Exited event loop" << "\n";
|
std::cout << __func__ << ": Exited event loop" << "\n";
|
||||||
shutdownSalmanoff();
|
shutdownSalmanoff();
|
||||||
}
|
}
|
||||||
|
catch (const OptionParser::Exception& e)
|
||||||
|
{
|
||||||
|
std::ostream &out = std::cout;
|
||||||
|
std::string outUsageMsg;
|
||||||
|
|
||||||
|
if (typeid(e) == typeid(OptionsParserError))
|
||||||
|
{
|
||||||
|
mrntt::exitCode = EXIT_FAILURE;
|
||||||
|
outUsageMsg = std::string(__func__) + ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << outUsageMsg << e.what() << std::endl;
|
||||||
|
mind.finalizeReq([]{
|
||||||
|
mrntt::mrntt->getIoService().stop();
|
||||||
|
});
|
||||||
|
self.getIoService().reset();
|
||||||
|
self.getIoService().run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
std::cerr << __func__ << ": Exception occurred: " << e.what()
|
std::cerr << __func__ << ": Exception occurred: " << e.what()
|
||||||
|
|||||||
+28
-6
@@ -1,6 +1,7 @@
|
|||||||
#include <opts.h>
|
#include <opts.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <exception>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -8,6 +9,28 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
OptionsParserError::OptionsParserError(
|
||||||
|
const std::string& errorMessage, const OptionParser& parser
|
||||||
|
)
|
||||||
|
: std::invalid_argument(errorMessage + "\n" + parser.getUsage())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* OptionsParserError::what() const noexcept
|
||||||
|
{
|
||||||
|
return std::invalid_argument::what();
|
||||||
|
}
|
||||||
|
|
||||||
|
JustPrintUsageNoError::JustPrintUsageNoError(const OptionParser& parser)
|
||||||
|
: message(parser.getUsage())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* JustPrintUsageNoError::what() const noexcept
|
||||||
|
{
|
||||||
|
return message.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
struct option OptionParser::longOptions[] = {
|
struct option OptionParser::longOptions[] = {
|
||||||
{"devicespec", required_argument, 0, 's'},
|
{"devicespec", required_argument, 0, 's'},
|
||||||
{"spec", required_argument, 0, 's'},
|
{"spec", required_argument, 0, 's'},
|
||||||
@@ -59,9 +82,9 @@ void OptionParser::parseArguments(int argc, char *argv[], char **envp)
|
|||||||
|
|
||||||
if (stat(optarg, &info) != 0 || !(info.st_mode & S_IFDIR))
|
if (stat(optarg, &info) != 0 || !(info.st_mode & S_IFDIR))
|
||||||
{
|
{
|
||||||
throw std::invalid_argument(
|
throw OptionsParserError(
|
||||||
std::string(__func__) + " - The specified path is not a "
|
std::string(__func__) + " - The specified path is not a "
|
||||||
"directory: " + optarg);
|
"directory: " + optarg, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
senseApiLibPath.push_back(optarg);
|
senseApiLibPath.push_back(optarg);
|
||||||
@@ -71,12 +94,11 @@ void OptionParser::parseArguments(int argc, char *argv[], char **envp)
|
|||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
printUsage = true;
|
throw JustPrintUsageNoError(*this);
|
||||||
return;
|
|
||||||
case '?':
|
case '?':
|
||||||
throw std::invalid_argument(
|
throw OptionsParserError(
|
||||||
std::string(__func__) + " - Invalid argument encountered: "
|
std::string(__func__) + " - Invalid argument encountered: "
|
||||||
+ std::string(argv[optind - 1]));
|
+ std::string(argv[optind - 1]), *this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user