From fffd611a52a79314e965ba214048280e12f1bd42 Mon Sep 17 00:00:00 2001 From: Tijmen van Nesselrooij Date: Fri, 29 Aug 2025 17:16:10 +0200 Subject: [PATCH] Improve cxxopts usage to avoid segfaults --- src/electricity-logger/main.cpp | 51 +++++++++++++++-------- src/solar-logger/main.cpp | 72 ++++++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/src/electricity-logger/main.cpp b/src/electricity-logger/main.cpp index ed7d0a4..40e9018 100644 --- a/src/electricity-logger/main.cpp +++ b/src/electricity-logger/main.cpp @@ -10,7 +10,26 @@ #include #include -std::optional 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 TryGetStringArgument(cxxopts::ParseResult const & parseResult, std::string const & name) +{ + if(!parseResult.contains(name)) + { + return {}; + } + + return parseResult[name].as_optional(); +} + +std::optional TryGetCommandlineArguments(int argc, char ** argv) { cxxopts::Options options( "electricity-logger", @@ -24,22 +43,25 @@ std::optional ExtractArgs(int argc, char ** argv) "Path to the sqlite3 database file", cxxopts::value()); - 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()); - - auto const connectionStringValue = args["connection-string"].as(); - Database db(connectionStringValue); + Database db(parameters->DatabaseConnectionString); db.Insert(data, std::time(nullptr)); return 0; diff --git a/src/solar-logger/main.cpp b/src/solar-logger/main.cpp index f87c210..9402546 100644 --- a/src/solar-logger/main.cpp +++ b/src/solar-logger/main.cpp @@ -21,7 +21,43 @@ namespace detail } } -std::optional 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 TryGetStringArgument(cxxopts::ParseResult const & parseResult, std::string const & name) +{ + if(!parseResult.contains(name)) + { + return {}; + } + + return parseResult[name].as_optional(); +} + +std::optional TryGetUnsignedArgument(cxxopts::ParseResult const & parseResult, std::string const & name) +{ + if(!parseResult.contains(name)) + { + return {}; + } + + return parseResult[name].as_optional(); +} + +std::optional TryGetCommandlineArguments(int argc, char ** argv) { cxxopts::Options options( "solar-logger", @@ -49,14 +85,30 @@ std::optional 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 FetchDataFromURL(const std::string & url, const unsigned int timeout) @@ -97,21 +149,19 @@ std::optional 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()); + Database db(parameters->DatabaseConnectionString); auto const now = std::time(nullptr); - auto const resourceType = args["type"].as(); + auto const resourceType = parameters->DeviceType; if(resourceType == "Zeverlution") { - auto const webResource = FetchDataFromURL(args["url"].as(), args["timeout"].as()); + 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(), args["timeout"].as()); + auto const webResource = FetchDataFromURL(parameters->DeviceUrl, parameters->DeviceFetchTimeOut); if(!webResource.has_value()) { return -1;