Closed JohnnyMorganz closed 1 year ago
Hi,
How about this? Does this solve your problem?
append()
on the flag
argument to specify that you want to use --flag
multiple timesassign_chars
and set the assignment character to ':'
Once parsing is done, you'll have a vector of strings, for each flag that was used. Now parse each one and separate out the KEY
and VALUE
using std::string::substr
.
#include "argparse.hpp"
std::pair<std::string, std::string> get_kvpair(const std::string &str) {
size_t pos = str.find("=");
if (pos != std::string::npos) {
std::string key = str.substr(0, pos);
std::string value = str.substr(pos + 1);
return std::make_pair(key, value);
} else {
throw std::invalid_argument(
"Invalid key-value pair, expected KEY=VALUE, instead got " + str);
}
}
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("kv");
program.add_argument("--flag")
.default_value<std::vector<std::string>>({})
.append();
program.set_assign_chars(":");
try {
program.parse_args(argc, argv);
} catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
}
const auto &flags = program.get<std::vector<std::string>>("--flag");
for (auto f : flags) {
/// Parse each "KEY=VALUE" string into KEY and VALUE pair
auto pair = get_kvpair(f);
/// Print it
std::cout << "Key(" << pair.first << ") = Value(" << pair.second << ")\n";
}
}
Example Output:
foo:bar $ ./main --flag:FOO=True --flag:BAR=False --flag:CMAKE_BUILD_TYPE=Release
Key(FOO) = Value(True)
Key(BAR) = Value(False)
Key(CMAKE_BUILD_TYPE) = Value(Release)
Hey, thanks for the quick response!
This looks promising. I haven't tried it yet, but would it still be able to handle standard flags with =
assign char?
I'm guessing I would need to also include =
as an assign char in the set
End result is the ability to parse something like this:
./tool --flag:FOO=BAR --config=path file.txt
I'm trying to repurpose some existing logic to try and use this library, but understandable if it's not possible to fit it all together.
Thanks again! I'll try it out in the next day or so
Correct. That should work.
You can add =
to the assign chars spec.
Here's the updated example:
#include "argparse.hpp"
std::pair<std::string, std::string> get_kvpair(const std::string &str) {
size_t pos = str.find("=");
if (pos != std::string::npos) {
std::string key = str.substr(0, pos);
std::string value = str.substr(pos + 1);
return std::make_pair(key, value);
} else {
throw std::invalid_argument(
"Invalid key-value pair, expected KEY=VALUE, instead got " + str);
}
}
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("kv");
// Typical usage: --flag:FOO=VALUE
program.add_argument("--flag")
.default_value<std::vector<std::string>>({})
.append();
// Path to config file
program.add_argument("--config");
// Input file
program.add_argument("input_file");
// Use ':' or '=' as the assignment character for arguments
program.set_assign_chars(":=");
try {
program.parse_args(argc, argv);
} catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
}
const auto &flags = program.get<std::vector<std::string>>("--flag");
for (auto f : flags) {
/// Parse each "KEY=VALUE" string into KEY and VALUE pair
auto pair = get_kvpair(f);
/// Print it
std::cout << "Key(" << pair.first << ") = Value(" << pair.second << ")\n";
}
if (program.is_used("--config")) {
std::cout << "Config: " << program.get<std::string>("--config") << "\n";
}
std::cout << "Input File: " << program.get<std::string>("input_file") << "\n";
}
foo:bar $ ./main --flag:FOO=True --flag:BAR=False --flag:CMAKE_BUILD_TYPE=Release file.txt
Key(FOO) = Value(True)
Key(BAR) = Value(False)
Key(CMAKE_BUILD_TYPE) = Value(Release)
Input File: file.txt
foo:bar $~/dev/argparse/include/argparse$ ./main --flag:FOO=True --flag:BAR=False --flag:CMAKE_BUILD_TYPE=Release --config=config_file_path file.txt
Key(FOO) = Value(True)
Key(BAR) = Value(False)
Key(CMAKE_BUILD_TYPE) = Value(Release)
Config: config_file_path
Input File: file.txt
Yes, this seems to be working perfectly. Thank you very much!
Hey, awesome project!
I have a CLI tool where I allow users to customize FFlags on the command line. It is essentially a "dictionary-like" structure, where a user can do
--flag:NAME=VALUE
:I could not figure out if there was a way to do this via this library. I could get part the way there by iterating through all the FFlags statically defined, and registering them individually as hidden arguments in the parser. However, I do not want unknown FFlags to error, and I want to allow it to be dynamic.
parse_known_args
stops it from erroring, but I do want any other unknown option to error.Essentially, any argument prefixed with
--flag:
should not error, and be handled as a K/V dictionary.Is it possible to do this right now?
Thank you!