Merge pull request #36 from morphis/feature/cli-valueless-flags

Add support for command line flags without value
This commit is contained in:
Simon Fels 2017-02-17 16:40:28 +01:00 • committed by GitHub
commit 1d60c2f1f5
2 changed files with 68 additions and 26 deletions

View file

@ -44,9 +44,10 @@ static constexpr const char* option = " --%1% %2%";
void add_to_desc_for_flags(po::options_description& desc, void add_to_desc_for_flags(po::options_description& desc,
const std::set<cli::Flag::Ptr>& flags) { const std::set<cli::Flag::Ptr>& flags) {
for (auto flag : flags) { for (auto flag : flags) {
auto v = po::value<std::string>()->notifier( po::value_semantic *spec = nullptr;
[flag](const std::string& s) { flag->notify(s); }); flag->specify_option(spec);
desc.add_options()(flag->name().as_string().c_str(), v, if (!spec) continue;
desc.add_options()(flag->name().as_string().c_str(), spec,
flag->description().as_string().c_str()); flag->description().as_string().c_str());
} }
} }

View file

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2016 Canonical, Ltd. * Copyright (C) 2016 Canonical, Ltd.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -31,6 +31,8 @@
#include "anbox/do_not_copy_or_move.h" #include "anbox/do_not_copy_or_move.h"
#include "anbox/optional.h" #include "anbox/optional.h"
#include <boost/program_options.hpp>
namespace anbox { namespace anbox {
namespace cli { namespace cli {
@ -76,16 +78,19 @@ typedef SizeConstrainedString<80> Description;
/// @brief Flag models an input parameter to a command. /// @brief Flag models an input parameter to a command.
class Flag : public DoNotCopyOrMove { class Flag : public DoNotCopyOrMove {
public: public:
typedef boost::program_options::value_semantic* Specification;
// Safe us some typing. // Safe us some typing.
typedef std::shared_ptr<Flag> Ptr; typedef std::shared_ptr<Flag> Ptr;
/// @brief notify announces a new value to the flag.
virtual void notify(const std::string& value) = 0;
/// @brief name returns the name of the Flag. /// @brief name returns the name of the Flag.
const Name& name() const; const Name& name() const;
/// @brief description returns a human-readable description of the flag. /// @brief description returns a human-readable description of the flag.
const Description& description() const; const Description& description() const;
/// @brief specify the program option of the flag.
virtual void specify_option(Specification& spec) = 0;
protected: protected:
/// @brief Flag creates a new instance, initializing name and description /// @brief Flag creates a new instance, initializing name and description
/// from the given values. /// from the given values.
@ -115,12 +120,15 @@ class TypedFlag : public Flag {
/// @brief value returns the optional value associated with the flag. /// @brief value returns the optional value associated with the flag.
const Optional<T>& value() const { return value_; } const Optional<T>& value() const { return value_; }
/// @brief notify tries to unwrap a value of type T from value. /// @brief Option generated by specify_option tries to unwrap a value
void notify(const std::string& s) override { /// of type T from value.
void specify_option(Flag::Specification& spec) override {
spec = boost::program_options::value<std::string>()->notifier([&](const std::string& s) {
std::stringstream ss{s}; std::stringstream ss{s};
T value; T value;
ss >> value; ss >> value;
value_ = value; value_ = value;
});
} }
private: private:
@ -141,11 +149,15 @@ class TypedReferenceFlag : public Flag {
TypedReferenceFlag(const Name& name, const Description& description, T& value) TypedReferenceFlag(const Name& name, const Description& description, T& value)
: Flag{name, description}, value_{value} {} : Flag{name, description}, value_{value} {}
/// @brief notify tries to unwrap a value of type T from value, /// @brief Option generated by specify_option tries to unwrap a value of type T
/// relying on operator>> to read from given string s. /// from value, relying on operator>> to read from given string s.
void notify(const std::string& s) override { void specify_option(Flag::Specification& spec) override {
spec = boost::program_options::value<std::string>()->notifier([&](const std::string& s) {
std::stringstream ss{s}; std::stringstream ss{s};
ss >> value_.get(); T value;
ss >> value;
value_ = value;
});
} }
private: private:
@ -153,9 +165,8 @@ class TypedReferenceFlag : public Flag {
}; };
/// @brief OptionalTypedReferenceFlag handles Optional<T> references, making /// @brief OptionalTypedReferenceFlag handles Optional<T> references, making
/// sure that /// sure that a value is always read on notify, even if the Optional<T> wasn't
/// a value is always read on notify, even if the Optional<T> wasn't initialized /// initialized previously.
/// previously.
template <typename T> template <typename T>
class OptionalTypedReferenceFlag : public Flag { class OptionalTypedReferenceFlag : public Flag {
public: public:
@ -165,18 +176,40 @@ class OptionalTypedReferenceFlag : public Flag {
Optional<T>& value) Optional<T>& value)
: Flag{name, description}, value_{value} {} : Flag{name, description}, value_{value} {}
/// @brief notify tries to unwrap a value of type T from value. /// @brief Option generated by specify_option tries to unwrap a value of
void notify(const std::string& s) override { /// type T from value.
void specify_option(Flag::Specification& spec) override {
spec = boost::program_options::value<std::string>()->notifier([&](const std::string& s) {
std::stringstream ss{s}; std::stringstream ss{s};
T value; T value;
ss >> value; ss >> value;
value_.get() = value; value_.get() = value;
});
} }
private: private:
std::reference_wrapper<Optional<T>> value_; std::reference_wrapper<Optional<T>> value_;
}; };
/// @brief BoolSwitchFlag implements Flag, updating the given mutable reference
/// to a boolean value.
class BoolSwitchFlag : public Flag {
public:
typedef std::shared_ptr<BoolSwitchFlag> Ptr;
BoolSwitchFlag(const Name& name, const Description& description, bool& value)
: Flag{name, description}, value_(value) {}
/// @brief Option generated by specify_option tries to unwrap a boolean
/// value from value.
void specify_option(Flag::Specification& spec) override {
spec = boost::program_options::bool_switch(&value_.get());
}
private:
std::reference_wrapper<bool> value_;
};
/// @brief Command abstracts an individual command available from the daemon. /// @brief Command abstracts an individual command available from the daemon.
class Command : public DoNotCopyOrMove { class Command : public DoNotCopyOrMove {
public: public:
@ -337,6 +370,14 @@ typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name,
return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value); return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value);
} }
/// @brief make_flag returns a flag with the given name and description,
/// updating the given boolean value.
inline BoolSwitchFlag::Ptr make_flag(const Name& name,
const Description &desc,
bool &value) {
return std::make_shared<BoolSwitchFlag>(name, desc, value);
}
} // namespace cli } // namespace cli
} // namespace anbox } // namespace anbox