Skip to content

Commit 3ee7e47

Browse files
committed
Implement BoundedValue
The idea is to check option boundaries while parsing to naturally deny wrong arguments, i.e. passing negative or too big values
1 parent 60cc633 commit 3ee7e47

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

include/popl.hpp

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,41 @@ class Switch : public Value<bool>
297297

298298

299299

300+
/// Bounded value option
301+
/**
302+
* Bounded value option
303+
* Checks if the value meets boundary predicate,
304+
* for example ( x > 3 && x < 99 )
305+
*/
306+
template <class T>
307+
class BoundedValue : public Value<T>
308+
{
309+
public:
310+
using Predicate = bool(*)(T);
311+
312+
/// Construct a BoundedValue Option
313+
/// @param short_name the option's short name. Must be empty or one character.
314+
/// @param long_name the option's long name. Can be empty.
315+
/// @param description the Option's description that will be shown in the help message
316+
/// @param predicate the option's correctness predicate, returns true if option is correct
317+
BoundedValue(const std::string& short_name, const std::string& long_name, const std::string& description, Predicate predicate);
318+
319+
/// Construct a BoundedValue Option
320+
/// @param short_name the option's short name. Must be empty or one character.
321+
/// @param long_name the option's long name. Can be empty.
322+
/// @param description the Option's description that will be shown in the help message
323+
/// @param predicate the option's correctness predicate, returns true if option is correct
324+
/// @param default_val the Option's default value
325+
/// @param assign_to pointer to a variable to assign the parsed command line value to
326+
BoundedValue(const std::string& short_name, const std::string& long_name, const std::string& description, Predicate predicate, const T& default_val, T* assign_to = nullptr);
327+
328+
protected:
329+
void parse(OptionName what_name, const char* value) override;
330+
331+
private:
332+
Predicate predicate_;
333+
};
334+
300335
using Option_ptr = std::shared_ptr<Option>;
301336

302337
/// OptionParser manages all Options
@@ -394,7 +429,8 @@ class invalid_option : public std::invalid_argument
394429
missing_argument,
395430
invalid_argument,
396431
too_many_arguments,
397-
missing_option
432+
missing_option,
433+
argument_out_of_bound,
398434
};
399435

400436
invalid_option(const Option* option, invalid_option::Error error, OptionName what_name, std::string value, const std::string& text)
@@ -755,7 +791,6 @@ inline void Value<T>::parse(OptionName what_name, const char* value)
755791
this->add_value(parsed_value);
756792
}
757793

758-
759794
template <class T>
760795
inline void Value<T>::update_reference()
761796
{
@@ -830,7 +865,31 @@ inline Argument Switch::argument_type() const
830865
return Argument::no;
831866
}
832867

868+
/// BoundedValue implementation /////////////////////////////////
833869

870+
template <class T>
871+
BoundedValue<T>::BoundedValue(const std::string& short_name, const std::string& long_name, const std::string& description, Predicate predicate)
872+
: Value<T>(short_name, long_name, description), predicate_(predicate)
873+
{
874+
875+
}
876+
877+
template <class T>
878+
BoundedValue<T>::BoundedValue(const std::string& short_name, const std::string& long_name, const std::string& description, Predicate predicate, const T& default_val, T* assign_to)
879+
: Value<T>(short_name, long_name, description, default_val, assign_to), predicate_(predicate)
880+
{
881+
882+
}
883+
884+
template <class T>
885+
inline void BoundedValue<T>::parse(OptionName what_name, const char* value)
886+
{
887+
Value<T>::parse(what_name, value);
888+
for ( const auto& v : this->values_)
889+
if ( !predicate_( v))
890+
throw invalid_option(this, invalid_option::Error::argument_out_of_bound, what_name, value,
891+
"argument is out of bound for " + this->name(what_name, true) + ": '" + value + "'");
892+
}
834893

835894
/// OptionParser implementation /////////////////////////////////
836895

test/test_main.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,28 @@ TEST_CASE("command line")
3535
}
3636
}
3737

38+
TEST_CASE("wrong predicated option")
39+
{
40+
OptionParser op("Allowed options");
41+
std::vector<const char*> args = {"popl", "-i", "9"};
42+
43+
auto int_option = op.add<BoundedValue<int>>("i", "int", "test for int value, should be divisible by 8", [](int x){ return x % 8 == 0; }, 64);
44+
45+
CHECK_THROWS_AS(op.parse(args.size(), args.data()), invalid_option);
46+
}
47+
48+
TEST_CASE("correct predicated option")
49+
{
50+
OptionParser op("Allowed options");
51+
std::vector<const char*> args = {"popl", "-i", "64"};
52+
53+
auto int_option = op.add<BoundedValue<int>>("i", "int", "test for int value, should be divisible by 8", [](int x){ return x % 8 == 0; }, 9);
54+
55+
op.parse(args.size(), args.data());
56+
REQUIRE(int_option->is_set() == true);
57+
REQUIRE(int_option->count() == 1);
58+
REQUIRE(int_option->value() == 64);
59+
}
3860

3961
TEST_CASE("config file")
4062
{

0 commit comments

Comments
 (0)