1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 12:13:52 +00:00

Add better enum support in the library (#233)

* add some notes about enums in the readme

add some helpers tests for enumerations

Add better enum support in the library

* fix Helpers Test for Enums
This commit is contained in:
Philip Top 2019-02-20 13:57:37 -08:00 committed by Henry Schreiner
parent 571fb07cfb
commit 6c645b55a1
6 changed files with 67 additions and 11 deletions

View File

@ -178,12 +178,12 @@ While all options internally are the same type, there are several ways to add an
```cpp ```cpp
app.add_option(option_name, app.add_option(option_name,
variable_to_bind_to, // bool, int, float, vector, or string-like variable_to_bind_to, // bool, int, float, vector, enum, or string-like
help_string="", help_string="",
default=false) default=false)
app.add_option_function<type>(option_name, app.add_option_function<type>(option_name,
function <void(const type &value)>, // int, float, vector, or string-like function <void(const type &value)>, // int, float, enum, vector, or string-like
help_string="") help_string="")
app.add_complex(... // Special case: support for complex numbers app.add_complex(... // Special case: support for complex numbers

View File

@ -1,15 +1,13 @@
#include <CLI/CLI.hpp> #include <CLI/CLI.hpp>
#include <map> #include <map>
#include <sstream>
enum class Level : int { High, Medium, Low }; enum class Level : int { High, Medium, Low };
int main(int argc, char **argv) { int main(int argc, char **argv) {
CLI::App app; CLI::App app;
std::map<std::string, Level> map = {{"High", Level::High}, {"Medium", Level::Medium}, {"Low", Level::Low}};
Level level; Level level;
std::map<std::string, Level> map = {{"High", Level::High}, {"Medium", Level::Medium}, {"Low", Level::Low}};
app.add_option("-l,--level", level, "Level settings") app.add_option("-l,--level", level, "Level settings")
->required() ->required()

View File

@ -27,10 +27,10 @@ inline std::string ini_join(std::vector<std::string> args) {
auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); }); auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
if(it == arg.end()) if(it == arg.end())
s << arg; s << arg;
else if(arg.find(R"(")") == std::string::npos) else if(arg.find_first_of('\"') == std::string::npos)
s << R"(")" << arg << R"(")"; s << '\"' << arg << '\"';
else else
s << R"(')" << arg << R"(')"; s << '\'' << arg << '\'';
} }
return s.str(); return s.str();

View File

@ -46,7 +46,7 @@ inline std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems; std::vector<std::string> elems;
// Check to see if empty string, give consistent result // Check to see if empty string, give consistent result
if(s.empty()) if(s.empty())
elems.emplace_back(""); elems.emplace_back();
else { else {
std::stringstream ss; std::stringstream ss;
ss.str(s); ss.str(s);
@ -70,6 +70,21 @@ template <typename T> std::string join(const T &v, std::string delim = ",") {
return s.str(); return s.str();
} }
/// Simple function to join a string from processed elements
template <typename T,
typename Callable,
typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::value>::type>
std::string join(const T &v, Callable func, std::string delim = ",") {
std::ostringstream s;
size_t start = 0;
for(const auto &i : v) {
if(start++ > 0)
s << delim;
s << func(i);
}
return s.str();
}
/// Join a string in reverse order /// Join a string in reverse order
template <typename T> std::string rjoin(const T &v, std::string delim = ",") { template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
std::ostringstream s; std::ostringstream s;

View File

@ -3,6 +3,7 @@
// Distributed under the 3-Clause BSD License. See accompanying // Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details. // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include "StringTools.hpp"
#include <exception> #include <exception>
#include <memory> #include <memory>
#include <string> #include <string>
@ -140,9 +141,16 @@ template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail
constexpr const char *type_name() { constexpr const char *type_name() {
return "VECTOR"; return "VECTOR";
} }
/// Print name for enumeration types
template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "ENUM";
}
/// Print for all other types
template <typename T, template <typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value, enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value &&
!std::is_enum<T>::value,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "TEXT"; return "TEXT";
@ -229,10 +237,22 @@ bool lexical_cast(std::string input, T &output) {
return true; return true;
} }
/// enumerations
template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
typename std::underlying_type<T>::type val;
bool retval = detail::lexical_cast(input, val);
if(!retval) {
return false;
}
output = static_cast<T>(val);
return true;
}
/// Non-string parsable /// Non-string parsable
template <typename T, template <typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
!std::is_assignable<T &, std::string>::value, !std::is_assignable<T &, std::string>::value && !std::is_enum<T>::value,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) { bool lexical_cast(std::string input, T &output) {
std::istringstream is; std::istringstream is;

View File

@ -527,6 +527,10 @@ TEST(Types, TypeName) {
std::string text2_name = CLI::detail::type_name<char *>(); std::string text2_name = CLI::detail::type_name<char *>();
EXPECT_EQ("TEXT", text2_name); EXPECT_EQ("TEXT", text2_name);
enum class test { test1, test2, test3 };
std::string enum_name = CLI::detail::type_name<test>();
EXPECT_EQ("ENUM", enum_name);
} }
TEST(Types, OverflowSmall) { TEST(Types, OverflowSmall) {
@ -617,6 +621,25 @@ TEST(Types, LexicalCastParsable) {
EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, output)); EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, output));
} }
TEST(Types, LexicalCastEnum) {
enum t1 : char { v1 = 5, v3 = 7, v5 = -9 };
t1 output;
EXPECT_TRUE(CLI::detail::lexical_cast("-9", output));
EXPECT_EQ(output, v5);
EXPECT_FALSE(CLI::detail::lexical_cast("invalid", output));
enum class t2 : uint64_t { enum1 = 65, enum2 = 45667, enum3 = 9999999999999 };
t2 output2;
EXPECT_TRUE(CLI::detail::lexical_cast("65", output2));
EXPECT_EQ(output2, t2::enum1);
EXPECT_FALSE(CLI::detail::lexical_cast("invalid", output2));
EXPECT_TRUE(CLI::detail::lexical_cast("9999999999999", output2));
EXPECT_EQ(output2, t2::enum3);
}
TEST(FixNewLines, BasicCheck) { TEST(FixNewLines, BasicCheck) {
std::string input = "one\ntwo"; std::string input = "one\ntwo";
std::string output = "one\n; two"; std::string output = "one\n; two";