Skip to content

Commit f4da463

Browse files
committed
Adding first draft of Sets
1 parent 048f968 commit f4da463

File tree

6 files changed

+158
-6
lines changed

6 files changed

+158
-6
lines changed

include/CLI/App.hpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "CLI/FormatterFwd.hpp"
2323
#include "CLI/Macros.hpp"
2424
#include "CLI/Option.hpp"
25+
#include "CLI/Set.hpp"
2526
#include "CLI/Split.hpp"
2627
#include "CLI/StringTools.hpp"
2728
#include "CLI/TypeTools.hpp"
@@ -603,6 +604,27 @@ class App {
603604
}
604605
#endif
605606

607+
/// Add set of options
608+
template <typename T>
609+
Option *add_cli_set(std::string option_name,
610+
T &member, ///< The selected member of the set
611+
Set options, ///< The set of possibilities
612+
std::string description = "") {
613+
614+
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
615+
616+
CLI::callback_t fun = [&member, options](CLI::results_t res) {
617+
member = res[0];
618+
return options.contains(member);
619+
};
620+
621+
Option *opt = add_option(option_name, fun, description, false);
622+
opt->type_name_fn([options]() {
623+
return std::string(detail::type_name<T>()) + " in {" + detail::join(options.get_items()) + "}";
624+
});
625+
return opt;
626+
}
627+
606628
/// Add set of options (No default, temp reference, such as an inline set)
607629
template <typename T>
608630
Option *add_set(std::string option_name,
@@ -1176,7 +1198,7 @@ class App {
11761198
}
11771199
/// Get a pointer to subcommand by index
11781200
App *get_subcommand(int index = 0) const {
1179-
if((index >= 0) && (index < subcommands_.size()))
1201+
if((index >= 0) && ((size_t)index < subcommands_.size()))
11801202
return subcommands_[index].get();
11811203
throw OptionNotFound(std::to_string(index));
11821204
}
@@ -1201,7 +1223,7 @@ class App {
12011223

12021224
/// Get an owning pointer to subcommand by index
12031225
CLI::App_p get_subcommand_ptr(int index = 0) const {
1204-
if((index >= 0) && (index < subcommands_.size()))
1226+
if((index >= 0) && ((size_t)index < subcommands_.size()))
12051227
return subcommands_[index];
12061228
throw OptionNotFound(std::to_string(index));
12071229
}

include/CLI/CLI.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#include "CLI/FormatterFwd.hpp"
2828

29+
#include "CLI/Set.hpp"
30+
2931
#include "CLI/Option.hpp"
3032

3133
#include "CLI/App.hpp"

include/CLI/Set.hpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#pragma once
2+
3+
// Distributed under the 3-Clause BSD License. See accompanying
4+
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5+
6+
#include <initializer_list>
7+
#include <memory>
8+
#include <string>
9+
#include <vector>
10+
11+
// CLI Library includes
12+
#include "CLI/StringTools.hpp"
13+
14+
namespace CLI {
15+
16+
class Set {
17+
protected:
18+
using storage_type = std::vector<std::string>;
19+
std::shared_ptr<storage_type> items_{std::make_shared<storage_type>()};
20+
std::shared_ptr<bool> ignore_case_{std::make_shared<bool>(false)};
21+
std::shared_ptr<bool> ignore_underscore_{std::make_shared<bool>(false)};
22+
23+
public:
24+
using iterator = storage_type::iterator;
25+
using const_iterator = storage_type::iterator;
26+
27+
Set(std::initializer_list<std::string> values) : items_(std::make_shared<storage_type>(values)) {}
28+
29+
/// Ignore case when matching
30+
Set *ignore_case(bool value = true) {
31+
*ignore_case_ = value;
32+
return this;
33+
}
34+
35+
/// Ignore underscores when matching
36+
Set *ignore_underscore(bool value = true) {
37+
*ignore_underscore_ = value;
38+
return this;
39+
}
40+
41+
/// Get the status of ignore case
42+
bool get_ignore_case() const { return *ignore_case_; }
43+
44+
/// Get the status of ignore underscore
45+
bool get_ignore_underscore() const { return *ignore_underscore_; }
46+
47+
/// Get the underlying vector (provisional)
48+
const storage_type &get_items() const { return *items_; }
49+
50+
/// Find a value in the set, taking into account ignore settings
51+
const_iterator find(const std::string &item) const {
52+
bool ignore_case = *ignore_case_;
53+
bool ignore_underscore = *ignore_underscore_;
54+
55+
return std::find_if(
56+
items_->begin(), items_->end(), [ignore_case, ignore_underscore, &item](std::string item_in_set) {
57+
std::string item_local = item;
58+
59+
if(ignore_case) {
60+
item_local = detail::to_lower(item_local);
61+
item_in_set = detail::to_lower(item_in_set);
62+
}
63+
64+
if(ignore_underscore) {
65+
item_local = detail::remove_underscore(item_local);
66+
item_in_set = detail::remove_underscore(item_in_set);
67+
}
68+
69+
return item_local == item_in_set;
70+
});
71+
}
72+
73+
/// Check to see if an item is in the set
74+
bool contains(const std::string &item) const { return find(item) != items_->end(); }
75+
76+
/// Insert an item at the end of the set
77+
bool insert(const std::string &item) {
78+
if(contains(item)) {
79+
return false;
80+
} else {
81+
items_->emplace_back(item);
82+
return true;
83+
}
84+
}
85+
86+
/// Remove an item by name from the set. Returns true if the item was originally in the set.
87+
///
88+
/// Note that this is not fast, but CLI11 sets should be mostly small and static
89+
bool discard(const std::string &item) {
90+
auto result = std::find(items_->begin(), items_->end(), item);
91+
if(result != items_->end()) {
92+
items_->erase(result);
93+
return true;
94+
} else {
95+
return false;
96+
}
97+
}
98+
99+
/// Remove an item by name from the set, throw an error if the item is not present.
100+
void remove(const std::string &item);
101+
102+
/// Get an item by number from the set
103+
const std::string &at(size_t n) const { return items_->at(n); }
104+
105+
/// Get an item by number from the set (non-const)
106+
std::string &at(size_t n) { return items_->at(n); }
107+
108+
/// Get an item by number from the set
109+
const std::string &operator[](size_t n) const { return (*items_)[n]; }
110+
111+
/// Get an item by number from the set (non-const)
112+
std::string &operator[](size_t n) { return (*items_)[n]; }
113+
};
114+
115+
} // namespace CLI

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ set(CLI11_TESTS
2626
IniTest
2727
SimpleTest
2828
AppTest
29+
SetTest
2930
CreationTest
3031
SubcommandTest
3132
HelpTest

tests/SetTest.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "app_helper.hpp"
2+
3+
TEST_F(TApp, SimpleSets) {
4+
std::string value;
5+
auto opt = app.add_cli_set("-s,--set", value, {"one", "two", "three"});
6+
args = {"-s", "one"};
7+
run();
8+
EXPECT_EQ(1u, app.count("-s"));
9+
EXPECT_EQ(1u, app.count("--set"));
10+
EXPECT_EQ(1u, opt->count());
11+
EXPECT_EQ(value, "one");
12+
}

tests/SubcommandTest.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -888,8 +888,8 @@ TEST_F(TApp, UnnamedSubMixExtras) {
888888
run();
889889
EXPECT_EQ(val, -3.0);
890890
EXPECT_EQ(val2, 5.93);
891-
EXPECT_EQ(app.remaining_size(), 2);
892-
EXPECT_EQ(sub->remaining_size(), 0);
891+
EXPECT_EQ(app.remaining_size(), 2u);
892+
EXPECT_EQ(sub->remaining_size(), 0u);
893893
}
894894

895895
TEST_F(TApp, UnnamedSubNoExtras) {
@@ -901,8 +901,8 @@ TEST_F(TApp, UnnamedSubNoExtras) {
901901
run();
902902
EXPECT_EQ(val, -3.0);
903903
EXPECT_EQ(val2, 5.93);
904-
EXPECT_EQ(app.remaining_size(), 0);
905-
EXPECT_EQ(sub->remaining_size(), 0);
904+
EXPECT_EQ(app.remaining_size(), 0u);
905+
EXPECT_EQ(sub->remaining_size(), 0u);
906906
}
907907

908908
TEST(SharedSubTests, SharedSubcommand) {

0 commit comments

Comments
 (0)