Merge pull request #48 from lichray/bind-in-action

Bind in action
This commit is contained in:
Pranav Srinivas Kumar 2019-11-14 06:55:57 -06:00 committed by GitHub
commit 5d1e80a7d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 11 deletions

View File

@ -91,8 +91,10 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60 PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right PointerAlignment: Right
RawStringFormats: RawStringFormats:
- Delimiter: pb - Language: TextProto
Language: TextProto Delimiters:
- 'pb'
- 'proto'
BasedOnStyle: google BasedOnStyle: google
ReflowComments: true ReflowComments: true
SortIncludes: true SortIncludes: true

View File

@ -40,6 +40,7 @@ SOFTWARE.
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <vector> #include <vector>
@ -73,7 +74,23 @@ using enable_if_container = std::enable_if_t<is_container_v<T>, T>;
template <typename T> template <typename T>
using enable_if_not_container = std::enable_if_t<!is_container_v<T>, T>; using enable_if_not_container = std::enable_if_t<!is_container_v<T>, T>;
} // namespace
template <class F, class Tuple, class Extra, size_t... I>
constexpr decltype(auto) apply_plus_one_impl(F &&f, Tuple &&t, Extra &&x,
std::index_sequence<I...>) {
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...,
std::forward<Extra>(x));
}
template <class F, class Tuple, class Extra>
constexpr decltype(auto) apply_plus_one(F &&f, Tuple &&t, Extra &&x) {
return details::apply_plus_one_impl(
std::forward<F>(f), std::forward<Tuple>(t), std::forward<Extra>(x),
std::make_index_sequence<
std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
} // namespace details
class ArgumentParser; class ArgumentParser;
@ -115,14 +132,22 @@ public:
return *this; return *this;
} }
template <class F> template <class F, class... Args>
auto action(F &&aAction) auto action(F &&aAction, Args &&... aBound)
-> std::enable_if_t<std::is_invocable_v<F, std::string const>, -> std::enable_if_t<std::is_invocable_v<F, Args..., std::string const>,
Argument &> { Argument &> {
if constexpr (std::is_void_v<std::invoke_result_t<F, std::string const>>) using action_type = std::conditional_t<
mAction.emplace<void_action>(std::forward<F>(aAction)); std::is_void_v<std::invoke_result_t<F, Args..., std::string const>>,
void_action, valued_action>;
if constexpr (sizeof...(Args) == 0)
mAction.emplace<action_type>(std::forward<F>(aAction));
else else
mAction.emplace<valued_action>(std::forward<F>(aAction)); mAction.emplace<action_type>(
[f = std::forward<F>(aAction),
tup = std::make_tuple(std::forward<Args>(aBound)...)](
std::string const &opt) mutable {
return details::apply_plus_one(f, tup, opt);
});
return *this; return *this;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <catch.hpp> #include <catch.hpp>
#include <argparse.hpp> #include <argparse.hpp>
#include <string_view>
TEST_CASE("Users can use default value inside actions", "[actions]") { TEST_CASE("Users can use default value inside actions", "[actions]") {
argparse::ArgumentParser program("test"); argparse::ArgumentParser program("test");
@ -39,3 +40,82 @@ TEST_CASE("Users can add actions that return nothing", "[actions]") {
REQUIRE(program.get<int>("button") == 42); REQUIRE(program.get<int>("button") == 42);
} }
} }
class Image {
public:
int w = 0, h = 0;
void resize(std::string_view geometry) {
std::stringstream s;
s << geometry;
s >> w;
s.ignore();
s >> h;
}
static auto create(int w, int h, std::string_view format) -> Image {
auto factor = [=] {
if (format == "720p")
return std::min(1280. / w, 720. / h);
else if (format == "1080p")
return std::min(1920. / w, 1080. / h);
else
return 1.;
}();
return {static_cast<int>(w * factor), static_cast<int>(h * factor)};
}
};
TEST_CASE("Users can bind arguments to actions", "[actions]") {
argparse::ArgumentParser program("test");
GIVEN("an default initialized object bounded by reference") {
Image img;
program.add_argument("--size").action(&Image::resize, std::ref(img));
WHEN("provided no command-line arguments") {
program.parse_args({"test"});
THEN("the object is not updated") {
REQUIRE(img.w == 0);
REQUIRE(img.h == 0);
}
}
WHEN("provided command-line arguments") {
program.parse_args({"test", "--size", "320x98"});
THEN("the object is updated accordingly") {
REQUIRE(img.w == 320);
REQUIRE(img.h == 98);
}
}
}
GIVEN("a command-line option waiting for the last argument in its action") {
program.add_argument("format").action(Image::create, 400, 300);
WHEN("provided such an argument") {
program.parse_args({"test", "720p"});
THEN("the option object is created as if providing all arguments") {
auto img = program.get<Image>("format");
REQUIRE(img.w == 960);
REQUIRE(img.h == 720);
}
}
WHEN("provided a different argument") {
program.parse_args({"test", "1080p"});
THEN("a different option object is created") {
auto img = program.get<Image>("format");
REQUIRE(img.w == 1440);
REQUIRE(img.h == 1080);
}
}
}
}