Skip to content

Commit

Permalink
add some notes about enums in the readme
Browse files Browse the repository at this point in the history
add some helpers tests for enumerations

Add better enum support in the library
  • Loading branch information
phlptp committed Feb 20, 2019
1 parent 571fb07 commit b734e80
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 11 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,12 @@ While all options internally are the same type, there are several ways to add an

```cpp
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="",
default=false)

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="")

app.add_complex(... // Special case: support for complex numbers
Expand Down
4 changes: 1 addition & 3 deletions examples/enum.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
#include <CLI/CLI.hpp>
#include <map>
#include <sstream>

enum class Level : int { High, Medium, Low };

int main(int argc, char **argv) {
CLI::App app;

std::map<std::string, Level> map = {{"High", Level::High}, {"Medium", Level::Medium}, {"Low", Level::Low}};

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")
->required()
Expand Down
6 changes: 3 additions & 3 deletions include/CLI/ConfigFwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()); });
if(it == arg.end())
s << arg;
else if(arg.find(R"(")") == std::string::npos)
s << R"(")" << arg << R"(")";
else if(arg.find_first_of('\"') == std::string::npos)
s << '\"' << arg << '\"';
else
s << R"(')" << arg << R"(')";
s << '\'' << arg << '\'';
}

return s.str();
Expand Down
17 changes: 16 additions & 1 deletion include/CLI/StringTools.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ inline std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
// Check to see if empty string, give consistent result
if(s.empty())
elems.emplace_back("");
elems.emplace_back();
else {
std::stringstream ss;
ss.str(s);
Expand All @@ -70,6 +70,21 @@ template <typename T> std::string join(const T &v, std::string delim = ",") {
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
template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
std::ostringstream s;
Expand Down
24 changes: 22 additions & 2 deletions include/CLI/TypeTools.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.

#include "StringTools.hpp"
#include <exception>
#include <memory>
#include <string>
Expand Down Expand Up @@ -140,9 +141,16 @@ template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail
constexpr const char *type_name() {
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,
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>
constexpr const char *type_name() {
return "TEXT";
Expand Down Expand Up @@ -229,10 +237,22 @@ bool lexical_cast(std::string input, T &output) {
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
template <typename T,
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>
bool lexical_cast(std::string input, T &output) {
std::istringstream is;
Expand Down
23 changes: 23 additions & 0 deletions tests/HelpersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,10 @@ TEST(Types, TypeName) {

std::string text2_name = CLI::detail::type_name<char *>();
EXPECT_EQ("TEXT", text2_name);

enum class test { test1, test2, test3 };
std::string enum_name = CLI::detail::type_name<test>();
EXPECT_EQ("ENUM", text2_name);
}

TEST(Types, OverflowSmall) {
Expand Down Expand Up @@ -617,6 +621,25 @@ TEST(Types, LexicalCastParsable) {
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) {
std::string input = "one\ntwo";
std::string output = "one\n; two";
Expand Down

0 comments on commit b734e80

Please sign in to comment.