Optional arguments to Opt using std::optional
There is code to handle std::optional via CLARA_CONFIG_OPTIONAL_TYPE; however Opt still is expecting an argument if none is given.
For example for logging: ./program, run with no logging (default), ./program -l enable logging, ./program -l file.log enable logging to named file.
This would make sense to use std::optional here.
For example, the default will not do anything; giving the "flag" -l will act like a pass the log through to std::clog; giving the optional with a filename -l /path/to/file.log will pass through to the /path/to/file.log.
std::ostream m_log{nullptr};
std::ofstream m_log_{};
clara::Opt{[&](std::optional<fs::path> filename) {
if (!filename) {
m_log.rdbuf(std::clog.rdbuf());
return clara::ParserResult::ok(clara::ParseResultType::Matched);
}
m_log_.open(*filename);
m_log.rdbuf(m_log_.rdbuf());
return clara::ParserResult::ok(clara::ParseResultType::Matched);
},
"filename"s}["-l"s]["--log"s]("enable logging [set log file {stderr}]"s);
My current work around is to use a class with a set of stream operators.
edited slightly to only show the logging
namespace utils {
// in utils.hxx
class log {
public:
log() = default;
explicit log(std::ostream& os)
: m_log{os.rdbuf()} {}
explicit log(fs::path const& path)
: m_log_{path}
, m_log{m_log_.rdbuf()} {}
template <typename T>
friend auto inline operator<<(utils::log& log, T const& value) -> utils::log& {
log.m_log << value;
return log;
}
friend auto operator>>(std::istream& is, log& log) -> std::istream&;
private:
std::ofstream m_log_{};
std::ostream m_log{nullptr};
};
// in utils.cxx
auto operator>>(std::istream& is, log& log) -> std::istream& {
auto buf = std::string{};
is >> buf;
// check if boolean value
auto const static negative = std::array{"0"s, "false"s, "off"s, "n"s, "no"s};
auto const static positive = std::array{"1"s, "true"s, "on"s, "y"s, "yes"s};
auto boolean = buf;
std::transform(std::begin(boolean), std::end(boolean), std::begin(boolean),
[](unsigned char c) { return std::tolower(c); });
if (std::find(std::begin(negative), std::end(negative), boolean) != std::end(negative)) {
// disable logging
} else if (std::find(std::begin(positive), std::end(positive), boolean) != std::end(positive)) {
// enable logging to stderr
log.m_log.rdbuf(std::clog.rdbuf());
} else {
// enable logging to file
auto const filename = fs::path{buf};
log.m_log_.open(filename);
log.m_log.rdbuf(log.m_log_.rdbuf());
}
return is;
}
} // namespace utils
// relevant bit of parser
bool m_help{false};
utils::log m_log{};
auto args = clara::Help(m_help)
| clara::Opt{m_log, "false|true|filename"s}["-l"s]["--log"s](
"<false> disable logging\n"
"<true> enable logging to stderr\n"
"<filename> enable logging to filename\t{false}"s);
As you can see it's not quite ideal; I also have to call it ./program -l true rather than ./program -l.
Considering Clara also has bool parsing, and std::optional, I think it would be a nice addition to the library.