From f84f17d719dd91b69a1cd0b0e698b375eca6211f Mon Sep 17 00:00:00 2001 From: Zhihao Yuan Date: Sat, 16 Nov 2019 14:30:05 -0600 Subject: [PATCH] Give ArgumentParser value semantics fixes: p-ranav/argparse#50 --- include/argparse.hpp | 21 ++++++++ test/main.cpp | 3 +- test/test_value_semantics.hpp | 95 +++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 test/test_value_semantics.hpp diff --git a/include/argparse.hpp b/include/argparse.hpp index a6eae15..724fa58 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -380,6 +380,27 @@ public: .implicit_value(true); } + ArgumentParser(ArgumentParser &&) noexcept = default; + ArgumentParser &operator=(ArgumentParser &&) = default; + + ArgumentParser(const ArgumentParser &other) + : mProgramName(other.mProgramName), + mPositionalArguments(other.mPositionalArguments), + mOptionalArguments(other.mOptionalArguments) { + for (auto it = begin(mPositionalArguments); it != end(mPositionalArguments); + ++it) + index_argument(it); + for (auto it = begin(mOptionalArguments); it != end(mOptionalArguments); + ++it) + index_argument(it); + } + + ArgumentParser &operator=(const ArgumentParser &other) { + auto tmp = other; + std::swap(*this, tmp); + return *this; + } + // Parameter packing // Call add_argument with variadic number of string arguments template Argument &add_argument(Targs... Fargs) { diff --git a/test/main.cpp b/test/main.cpp index 0ffa4a6..d6cafe0 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -12,4 +12,5 @@ #include #include #include -#include \ No newline at end of file +#include +#include diff --git a/test/test_value_semantics.hpp b/test/test_value_semantics.hpp new file mode 100644 index 0000000..3b39fab --- /dev/null +++ b/test/test_value_semantics.hpp @@ -0,0 +1,95 @@ +#pragma once +#include +#include + +TEST_CASE("ArgumentParser is MoveConstructible and MoveAssignable", + "[value_semantics]") { + GIVEN("a parser that has two arguments") { + argparse::ArgumentParser parser("test"); + parser.add_argument("foo"); + parser.add_argument("-f"); + + WHEN("move construct a new parser from it") { + auto new_parser = std::move(parser); + + THEN("the old parser replaces the new parser") { + new_parser.parse_args({"test", "bar", "-f", "nul"}); + + REQUIRE(new_parser.get("foo") == "bar"); + REQUIRE(new_parser.get("-f") == "nul"); + } + } + + WHEN("move assign a parser prvalue to it") { + parser = argparse::ArgumentParser("test"); + + THEN("the old parser is replaced") { + REQUIRE_THROWS_AS(parser.parse_args({"test", "bar"}), + std::runtime_error); + REQUIRE_THROWS_AS(parser.parse_args({"test", "-f", "nul"}), + std::runtime_error); + REQUIRE_NOTHROW(parser.parse_args({"test"})); + } + } + } +} + +TEST_CASE("ArgumentParser is CopyConstructible and CopyAssignable", + "[value_semantics]") { + GIVEN("a parser that has two arguments") { + argparse::ArgumentParser parser("test"); + parser.add_argument("foo"); + parser.add_argument("-f"); + + WHEN("copy construct a new parser from it") { + auto new_parser = parser; + + THEN("the new parser has the old parser's capability") { + new_parser.parse_args({"test", "bar", "-f", "nul"}); + + REQUIRE(new_parser.get("foo") == "bar"); + REQUIRE(new_parser.get("-f") == "nul"); + + AND_THEN("but does not share states with the old parser") { + REQUIRE_THROWS_AS(parser.get("foo"), std::logic_error); + REQUIRE_THROWS_AS(parser.get("-f"), std::logic_error); + } + } + + AND_THEN("the old parser works as a distinct copy") { + new_parser.parse_args({"test", "toe", "-f", "/"}); + + REQUIRE(new_parser.get("foo") == "toe"); + REQUIRE(new_parser.get("-f") == "/"); + } + } + + WHEN("copy assign a parser lvalue to it") { + argparse::ArgumentParser optional_parser("test"); + optional_parser.add_argument("-g"); + parser = optional_parser; + + THEN("the old parser is replaced") { + REQUIRE_THROWS_AS(parser.parse_args({"test", "bar"}), + std::runtime_error); + REQUIRE_THROWS_AS(parser.parse_args({"test", "-f", "nul"}), + std::runtime_error); + REQUIRE_NOTHROW(parser.parse_args({"test"})); + REQUIRE_NOTHROW(parser.parse_args({"test", "-g", "nul"})); + + AND_THEN("but does not share states with the other parser") { + REQUIRE(parser.get("-g") == "nul"); + REQUIRE_THROWS_AS(optional_parser.get("-g"), std::logic_error); + } + } + + AND_THEN("the other parser works as a distinct copy") { + REQUIRE_NOTHROW(optional_parser.parse_args({"test"})); + REQUIRE_NOTHROW(optional_parser.parse_args({"test", "-g", "nul"})); + REQUIRE_THROWS_AS( + optional_parser.parse_args({"test", "bar", "-g", "nul"}), + std::runtime_error); + } + } + } +}