-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[suggestion] Check that .parse()
is called.
#168
Comments
Thanks for the input, sounds like a good idea! |
I played around a bit: diff --git a/include/sharg/parser.hpp b/include/sharg/parser.hpp
index b9ffe71..be4c54c 100644
--- a/include/sharg/parser.hpp
+++ b/include/sharg/parser.hpp
@@ -211,12 +211,17 @@ public:
init(argc, argv);
}
- //!\brief The destructor.
- ~parser()
+ /*!\brief The destructor.
+ * \throws sharg::design_error if sharg::parser::parse() was not called.
+ */
+ ~parser() noexcept(false)
{
// wait for another 3 seconds
if (version_check_future.valid())
version_check_future.wait_for(std::chrono::seconds(3));
+
+ if (!parse_was_called && std::uncaught_exceptions() == 0)
+ throw design_error{"The function parse() was not called for the app " + info.app_name + "!"};
}
//!\}
@@ -404,7 +409,7 @@ public:
void parse()
{
if (parse_was_called)
- throw design_error("The function parse() must only be called once!");
+ throw design_error{"The function parse() must only be called once for the app " + info.app_name + "!"};
detail::version_checker app_version{info.app_name, info.version, info.url};
The problem is that stuff like #include <ctime>
#include <sharg/parser.hpp>
int main()
{
std::string option_value;
char const * argv[] = {"./parser_test", "--version-check", "false", "-s", "option_string"};
sharg::parser parser{"test_parser", 5, argv};
parser.add_option(option_value, sharg::config{.short_id = 's'});
std::srand(std::time(nullptr));
int const random_variable = std::rand();
try
{
if (random_variable % 3 != 0)
throw std::invalid_argument{"moo"};
}
catch (std::exception const &)
{
return 1;
}
parser.parse();
return 0;
} wouldn't work anymore. The example is a bit weird, but the basic principle is that if calling $ ./example || echo $? # std::invalid_argument is thrown and caught
terminate called after throwing an instance of 'sharg::design_error'
what(): The function parse() was not called for the app test_parser!
Aborted
134 # != 1
$ ./example || echo $? # std::invalid_argument is not thrown You could, e.g., add another try/catch starting from I can ensure with I am not sure if this can be implemented without breaking any use cases, and also how much we should care about maybe-esoteric use cases. P.S.: For the tests, we can just set |
Yeah, I don't understand in which case you need an exit-path in the application between parser construction and calling
Yes, that's the point :)
Yep, that's something you always need to do.
I do this, but I am not seeing the problem. To invoke the subcommand parser, I need to have called the main parser's
I think there is a clear intention of how the parser can and should be used, and implementing this check is in line with that. If it breaks user code, they were doing something funky before (or likely have a bug in their code). And they can easily fix it! |
Hmm interesting edge case. I'd also consider this unlikely, since parsing is usually done before anything else.
Would that "fix" your example? ("fix" as in "well there is a I'm not sure if we want/can to enforce that " |
I think a few things have been conflated here. It is not relevant if an exception is thrown before iff that exception is caught. The two actual problems are:
Like I said, I think the API is
If you don't call parse, you have a bug. Or what would be the use-case for creating a parser that is not parsed? Is there anything else you can do with it? That having been said, this is just a minor convenience feature, and we shouldn't spend more time discussing it. If you are not convinced, just feel free to close this issue! |
I'll discuss it with @smehringer soon. I think we can add it. Maybe after changing the ctor to take a config, so we don't have to adjust the tests twice. Adjusting the tests for this feature is a bit finicky, because we have to use the test accessor to allow not calling parse where we don't want to call parse. |
After #238, this is probably a lot more straightforward. Only For the tests, we could have the test accessor set |
a small convenience feature: it is easy to forget calling
.parse()
. It would help if the destructor of the parser checks if the function was called and throws alogic_error("You forgot to call .parse()")
if it was not the case.Note, that you explicitly need to mark a destructor as
noexcept(false)
if you want to throw from it.The text was updated successfully, but these errors were encountered: