Improve cxxopts usage to avoid segfaults

This commit is contained in:
2025-08-29 17:16:10 +02:00
parent 4166a05a7f
commit fffd611a52
2 changed files with 96 additions and 27 deletions

View File

@@ -10,7 +10,26 @@
#include <stdexcept>
#include <string>
std::optional<cxxopts::ParseResult> ExtractArgs(int argc, char ** argv)
struct CommandlineParameters
{
CommandlineParameters(std::string const & serialDevicePath, std::string const & databaseConnectionString)
: DeviceType(serialDevicePath), DatabaseConnectionString(databaseConnectionString) { };
std::string DeviceType;
std::string DatabaseConnectionString;
};
std::optional<std::string> TryGetStringArgument(cxxopts::ParseResult const & parseResult, std::string const & name)
{
if(!parseResult.contains(name))
{
return {};
}
return parseResult[name].as_optional<std::string>();
}
std::optional<CommandlineParameters> TryGetCommandlineArguments(int argc, char ** argv)
{
cxxopts::Options options(
"electricity-logger",
@@ -24,22 +43,25 @@ std::optional<cxxopts::ParseResult> ExtractArgs(int argc, char ** argv)
"Path to the sqlite3 database file",
cxxopts::value<std::string>());
if(argc == 1)
{
std::cout << options.help() << std::endl;
return {};
}
try
{
auto const parsed = options.parse(argc, argv);
return parsed;
auto const parseResult = options.parse(argc, argv);
auto const serialDevicePath = TryGetStringArgument(parseResult, "serial-device");
auto const databaseConnectionString = TryGetStringArgument(parseResult, "connection-string");
if(serialDevicePath.has_value() && databaseConnectionString.has_value())
{
return CommandlineParameters(serialDevicePath.value(), databaseConnectionString.value());
}
}
catch(cxxopts::exceptions::exception const & e)
{
spdlog::error(e.what());
return {};
}
std::cout << options.help() << std::endl;
return {};
}
DSMR::Data ReadData(std::string const & devicePath)
@@ -100,18 +122,15 @@ DSMR::Data ReadData(std::string const & devicePath)
int main(int argc, char ** argv)
{
auto const maybeArgs = ExtractArgs(argc, argv);
if(!maybeArgs.has_value())
auto const parameters = TryGetCommandlineArguments(argc, argv);
if(!parameters.has_value())
{
return 1;
}
auto const & args = maybeArgs.value();
auto const data = ReadData(parameters->DeviceType);
auto const data = ReadData(args["serial-device"].as<std::string>());
auto const connectionStringValue = args["connection-string"].as<std::string>();
Database db(connectionStringValue);
Database db(parameters->DatabaseConnectionString);
db.Insert(data, std::time(nullptr));
return 0;

View File

@@ -21,7 +21,43 @@ namespace detail
}
}
std::optional<cxxopts::ParseResult> ExtractArgs(int argc, char ** argv)
struct CommandlineParameters
{
CommandlineParameters(
std::string const & deviceType,
std::string const & deviceUrl,
unsigned const deviceFetchTimeOut,
std::string const & databaseConnectionString)
: DeviceType(deviceType), DeviceUrl(deviceUrl), DeviceFetchTimeOut(deviceFetchTimeOut),
DatabaseConnectionString(databaseConnectionString) { };
std::string DeviceType;
std::string DeviceUrl;
unsigned DeviceFetchTimeOut;
std::string DatabaseConnectionString;
};
std::optional<std::string> TryGetStringArgument(cxxopts::ParseResult const & parseResult, std::string const & name)
{
if(!parseResult.contains(name))
{
return {};
}
return parseResult[name].as_optional<std::string>();
}
std::optional<unsigned> TryGetUnsignedArgument(cxxopts::ParseResult const & parseResult, std::string const & name)
{
if(!parseResult.contains(name))
{
return {};
}
return parseResult[name].as_optional<unsigned>();
}
std::optional<CommandlineParameters> TryGetCommandlineArguments(int argc, char ** argv)
{
cxxopts::Options options(
"solar-logger",
@@ -49,14 +85,30 @@ std::optional<cxxopts::ParseResult> ExtractArgs(int argc, char ** argv)
try
{
auto const parsed = options.parse(argc, argv);
return parsed;
auto const parseResult = options.parse(argc, argv);
auto const deviceType = TryGetStringArgument(parseResult, "type");
auto const deviceUrl = TryGetStringArgument(parseResult, "url");
auto const deviceFetchTimeOut = TryGetUnsignedArgument(parseResult, "timeout");
auto const databaseConnectionString = TryGetStringArgument(parseResult, "connection-string");
if(deviceType.has_value() && deviceUrl.has_value() && deviceFetchTimeOut.has_value()
&& databaseConnectionString.has_value())
{
return CommandlineParameters(
deviceType.value(),
deviceUrl.value(),
deviceFetchTimeOut.value(),
databaseConnectionString.value());
}
}
catch(cxxopts::exceptions::exception const & e)
{
spdlog::error(e.what());
return {};
}
std::cout << options.help() << std::endl;
return {};
}
std::optional<std::string> FetchDataFromURL(const std::string & url, const unsigned int timeout)
@@ -97,21 +149,19 @@ std::optional<std::string> FetchDataFromURL(const std::string & url, const unsig
int main(int argc, char ** argv)
{
auto const maybeArgs = ExtractArgs(argc, argv);
if(!maybeArgs.has_value())
auto const parameters = TryGetCommandlineArguments(argc, argv);
if(!parameters.has_value())
{
return 1;
}
auto const & args = maybeArgs.value();
Database db(args["connection-string"].as<std::string>());
Database db(parameters->DatabaseConnectionString);
auto const now = std::time(nullptr);
auto const resourceType = args["type"].as<std::string>();
auto const resourceType = parameters->DeviceType;
if(resourceType == "Zeverlution")
{
auto const webResource = FetchDataFromURL(args["url"].as<std::string>(), args["timeout"].as<unsigned>());
auto const webResource = FetchDataFromURL(parameters->DeviceUrl, parameters->DeviceFetchTimeOut);
if(!webResource.has_value())
{
return -1;
@@ -136,7 +186,7 @@ int main(int argc, char ** argv)
}
else if(resourceType == "Envoy")
{
auto const webResource = FetchDataFromURL(args["url"].as<std::string>(), args["timeout"].as<unsigned>());
auto const webResource = FetchDataFromURL(parameters->DeviceUrl, parameters->DeviceFetchTimeOut);
if(!webResource.has_value())
{
return -1;