Skip to content

Commit

Permalink
Updated README about custom help messages and updated single include
Browse files Browse the repository at this point in the history
  • Loading branch information
p-ranav committed Apr 28, 2021
1 parent f23342a commit bafe37e
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 50 deletions.
43 changes: 39 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ STRUCTOPT(Git, config, init);

int main(int argc, char *argv[]) {


try {
auto options = structopt::app("my_app").parse<Git>(argc, argv);

Expand Down Expand Up @@ -941,10 +941,10 @@ File : foo.txt

### Printing Help

`structopt` will insert two optional arguments for the user: `help` and `version`.
`structopt` will insert two optional arguments for the user: `help` and `version`.

* Using `-h` or `--help` will print the help message and exit.
* Using `-v` or `--version` will print the program version and exit.
* Using `-v` or `--version` will print the program version and exit.

```cpp
#include <structopt/app.hpp>
Expand Down Expand Up @@ -988,7 +988,42 @@ foo@bar:~$ ./main -v
1.0.3
```

***NOTE*** Admittedly, the above help message doesn't look great; none of the arguments have a description - something that is configurable in other argument parsers. `structopt` does its best to infer details about arguments from the user-defined struct including argument name, data type, and argument type. Unforunately, `structopt` (for now) does not provide any API to the user to configure (e.g., by providing a map) documentation for each of the fields in the struct.
### Printing CUSTOM Help Message

`structopt` allows users to provide a custom help messages. Simply pass in your custom help as a string argument to `structopt::app`

```cpp
#include <structopt/app.hpp>

struct Options {
// positional arguments
std::string input_file;
std::string output_file;

// optional arguments
std::optional<std::string> bind_address;

// remaining arguments
std::vector<std::string> files;
};
STRUCTOPT(Options, input_file, output_file, bind_address, files);

int main(int argc, char *argv[]) {

try {
const std::string& custom_help = "Usage: ./my_app input_file output_file [--bind-address BIND_ADDRESS] [files...]\n";
auto options = structopt::app("my_app", "1.0.3", custom_help).parse<Options>(argc, argv);
} catch (structopt::exception &e) {
std::cout << e.what() << "\n";
std::cout << e.help();
}
}
```
```console
foo@bar:~$ ./main -h
Usage: ./my_app input_file output_file [--bind-address BIND_ADDRESS] [files...]
```

## Building Samples and Tests

Expand Down
100 changes: 54 additions & 46 deletions single_include/structopt/structopt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,7 @@ namespace details {
struct visitor {
std::string name;
std::string version;
std::optional<std::string> help;
std::vector<std::string> field_names;
std::deque<std::string> positional_field_names; // mutated by parser
std::deque<std::string> positional_field_names_for_help;
Expand All @@ -1915,6 +1916,9 @@ struct visitor {
explicit visitor(const std::string &name, const std::string &version)
: name(name), version(version) {}

explicit visitor(const std::string &name, const std::string &version, const std::string& help)
: name(name), version(version), help(help) {}

// Visitor function for std::optional - could be an option or a flag
template <typename T>
inline typename std::enable_if<structopt::is_specialization<T, std::optional>::value,
Expand Down Expand Up @@ -1967,63 +1971,67 @@ struct visitor {
}

void print_help(std::ostream &os) const {
os << "\nUSAGE: " << name << " ";
if (help.has_value() && help.value().size() > 0) {
os << help.value();
} else {
os << "\nUSAGE: " << name << " ";

if (flag_field_names.empty() == false) {
os << "[FLAGS] ";
}
if (flag_field_names.empty() == false) {
os << "[FLAGS] ";
}

if (optional_field_names.empty() == false) {
os << "[OPTIONS] ";
}
if (optional_field_names.empty() == false) {
os << "[OPTIONS] ";
}

if (nested_struct_field_names.empty() == false) {
os << "[SUBCOMMANDS] ";
}
if (nested_struct_field_names.empty() == false) {
os << "[SUBCOMMANDS] ";
}

for (auto &field : positional_field_names_for_help) {
os << field << " ";
}
for (auto &field : positional_field_names_for_help) {
os << field << " ";
}

if (flag_field_names.empty() == false) {
os << "\n\nFLAGS:\n";
for (auto &flag : flag_field_names) {
os << " -" << flag[0] << ", --" << flag << "\n";
if (flag_field_names.empty() == false) {
os << "\n\nFLAGS:\n";
for (auto &flag : flag_field_names) {
os << " -" << flag[0] << ", --" << flag << "\n";
}
} else {
os << "\n";
}
} else {
os << "\n";
}

if (optional_field_names.empty() == false) {
os << "\nOPTIONS:\n";
for (auto &option : optional_field_names) {
if (optional_field_names.empty() == false) {
os << "\nOPTIONS:\n";
for (auto &option : optional_field_names) {

// Generate kebab case and present as option
auto kebab_case = option;
details::string_replace(kebab_case, "_", "-");
std::string long_form = "";
if (kebab_case != option) {
long_form = kebab_case;
} else {
long_form = option;
}
// Generate kebab case and present as option
auto kebab_case = option;
details::string_replace(kebab_case, "_", "-");
std::string long_form = "";
if (kebab_case != option) {
long_form = kebab_case;
} else {
long_form = option;
}

os << " -" << option[0] << ", --" << long_form << " <" << option << ">"
<< "\n";
os << " -" << option[0] << ", --" << long_form << " <" << option << ">"
<< "\n";
}
}
}

if (nested_struct_field_names.empty() == false) {
os << "\nSUBCOMMANDS:\n";
for (auto &sc : nested_struct_field_names) {
os << " " << sc << "\n";
if (nested_struct_field_names.empty() == false) {
os << "\nSUBCOMMANDS:\n";
for (auto &sc : nested_struct_field_names) {
os << " " << sc << "\n";
}
}
}

if (positional_field_names_for_help.empty() == false) {
os << "\nARGS:\n";
for (auto &arg : positional_field_names_for_help) {
os << " " << arg << "\n";
if (positional_field_names_for_help.empty() == false) {
os << "\nARGS:\n";
for (auto &arg : positional_field_names_for_help) {
os << " " << arg << "\n";
}
}
}
}
Expand Down Expand Up @@ -2980,8 +2988,8 @@ class app {
details::visitor visitor;

public:
explicit app(const std::string &name, const std::string &version = "")
: visitor(name, version) {}
explicit app(const std::string &name, const std::string &version = "", const std::string& help = "")
: visitor(name, version, help) {}

template <typename T> T parse(const std::vector<std::string> &arguments) {
T argument_struct = T();
Expand Down

0 comments on commit bafe37e

Please sign in to comment.