docopt / docopt.cpp

C++11 port of docopt
Boost Software License 1.0
1.04k stars 146 forks source link

Added ValueOr functions to ease usage of docopt in -fno-exceptions environment #137

Open Dadie opened 3 years ago

Dadie commented 3 years ago

While parsing of the documentation with docopt::docopt() is -fno-exceptions environment friendly, accessing any parsed value is not and requires some boilerplate code like

std::map< std::string, docopt::value > args;
{
    args = docopt::docopt(USAGE,
                { argv + 1, argv + argc },
                true,          // show help if requested
                "my_programm 1.0.0"); // version string
}
/* ... */
auto v = args.contains("123") ? 
              (
                  args.at("123").isString() ? 
                  args.at("123").asString() 
                  : 
                  "Fallback Value" 
              )  
              : 
              "Fallback Value";

which can at least be simplified like this with the PR

std::map< std::string, docopt::value > args;
{
    args = docopt::docopt(USAGE,
                { argv + 1, argv + argc },
                true,          // show help if requested
                "my_programm 1.0.0"); // version string
}
/* ... */
auto v = args.contains("123") ? 
              args.at("123").asStringOr("Fallback Value") 
              : 
              "Fallback Value";

or for those who don't care about unnecessary allocations like this

std::map< std::string, docopt::value > args;
{
    args = docopt::docopt(USAGE,
                { argv + 1, argv + argc },
                true,          // show help if requested
                "my_programm 1.0.0"); // version string
}
/* ... */
auto v = args["123"].asStringOr("Fallback Value");
Dadie commented 2 years ago

I'd love to add some tests for the functions but I'm not sure where I should add them as the current test run_test.py using the compiled run_testcase.cpp doesn't seem to be optimal to test those functions.

I'd assume the following cases would be helpful:

const std::string test_str = "STR";
const std::string test_other_str = "OTHER STR";
const std::string test_num_str = "123";
const std::vector<std::string> test_str_list = {test_str};
const std::vector<std::string> test_other_str_list = {test_other_str};

// Case 0: Bool Value (true)
const auto v_true = docopt::value(true);
assert(v_true.asBoolOr(true) == true); // <-- This should not use the Or-Value
assert(v_true.asBoolOr(false) == true); // <-- This should not use the Or-Value
assert(v_true.asLongOr(-1) == -1);
assert(v_true.asStringOr(test_str) == test_str);
assert(v_true.asStringListOr(test_str_list) == test_str_list);

// Case 1: Bool Value (false)
const auto v_false = docopt::value(false);
assert(v_false.asBoolOr(true) == false); // <-- This should not use the Or-Value
assert(v_false.asBoolOr(false) == false); // <-- This should not use the Or-Value
assert(v_false.asLongOr(-1) == -1);
assert(v_false.asLongOr(54321) == 54321);
assert(v_false.asStringOr(test_str) == test_str);
assert(v_false.asStringListOr(test_str_list) == test_str_list);

// Case 2: Long Value
const auto v_long = docopt::value(12345);
assert(v_long.asBoolOr(true) == true);
assert(v_long.asBoolOr(false) == false);
assert(v_long.asLongOr(-1) == 12345); // <-- This should not use the Or-Value
assert(v_long.asLongOr(54321) == 12345); // <-- This should not use the Or-Value
assert(v_long.asStringOr(test_str) == test_str);
assert(v_long.asStringListOr(test_str_list) == test_str_list);

// Case 3: String (Non-Numeric)
const auto v_str = docopt::value(test_other_str);
assert(v_str.asBoolOr(true) == true);
assert(v_str.asBoolOr(false) == false);
assert(v_str.asLongOr(-1) == -1);
assert(v_str.asLongOr(54321) == 54321);
assert(v_str.asStringOr(test_str) == test_other_str); // <-- This should not use the Or-Value
assert(v_str.asStringListOr(test_str_list) == test_str_list);

// Case 4: String (Numeric)
const auto v_num_str = docopt::value(test_num_str);
assert(v_num_str.asBoolOr(true) == true);
assert(v_num_str.asBoolOr(false) == false);
assert(v_num_str.asLongOr(-1) == 123); // <-- This should not use the Or-Value
assert(v_num_str.asLongOr(54321) == 123); // <-- This should not use the Or-Value
assert(v_num_str.asStringOr(test_str) == test_num_str); // <-- This should not use the Or-Value
assert(v_num_str.asStringListOr(test_str_list) == test_str_list);

// Case 5: String List
const auto v_str_list = docopt::value(test_other_str_list);
assert(v_str_list.asBoolOr(true) == true);
assert(v_str_list.asBoolOr(false) == false);
assert(v_str_list.asLongOr(-1) == -1);
assert(v_str_list.asLongOr(54321) == 54321);
assert(v_str_list.asStringOr(test_str) == test_other_str);
assert(v_str_list.asStringListOr(test_str_list) == test_other_str_list); // <-- This should not use the Or-Value