I am using boost add_options()
like this way:
("setclient", po::value<std::string>(), "<awsipaddress>[:awsport[:mystring]] Change the settings.")
This works well if I use:
./mycli —setclient 192.68.1.1:8888:ui
I want to allow special character like
[
and ]
. The usage will be like
./mycli --setclient [192.68.1.1]:8888:ui
Try out: I changed this
("setclient", po::value<std::string>(), "<awsipaddress>[:awsport[:mystring]] Change the settings.”)
to
("setclient", po::value<std::string>(), "<[awsipaddress]:>[:awsport[:mystring]] Change the settings.")
But doesn’t work for me
Mentioned in the problem statement my try out
The edited code is a descriptive text only, just intended to be printed as usage instructions. It doesn't encode a grammar or anything. So any changes to it don't matter.
The commenter has a good point that might get you half-way: the []
characters will often be special to the shell you're using. Wrap them in quotes e.g.:
./mycli --setclient '[192.68.1.1]:8888:ui'
or
./mycli --setclient "[192.68.1.1]:8888:ui"
Now, to actually support the additional characters you need to have a parser that expects the [
and ]
characters added. If that's not yet the case, please update with a self-contained example (that correctly handles the old syntax) and we can tell you what is missing.
With the simplest form:
#include <boost/program_options.hpp>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char** argv) {
po::options_description desc;
desc.add_options() //
("setclient", po::value<std::string>(),
"<awsipaddress>[:awsport[:mystring]] Change the settings.") //
; //
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
std::cout << "Got: '" << vm["setclient"].as<std::string>() <<"'\n";
}
Prints
./a.out --setclient 192.68.1.1:8888:ui
Got: '192.68.1.1:8888:ui'
./a.out --setclient [192.68.1.1]:8888:ui
Got: '[192.68.1.1]:8888:ui'
Guessing that you MIGHT require []
to allow ipv6 addresses (as they contain :
characters), have a look at What is the nicest way to parse this in C++?, and a new live demo integrating that in your program-options:
#include <boost/asio/ip/address.hpp>
#include <boost/program_options.hpp>
#include <iomanip>
#include <iostream>
namespace po = boost::program_options;
using boost::asio::ip::address;
struct Endpoint {
address address_;
uint16_t port_;
std::string name_;
static Endpoint parse_endpoint(std::string_view);
};
int main(int argc, char** argv) {
po::options_description desc;
std::string setclient;
desc.add_options() //
("setclient", po::value<std::string>(&setclient),
"<awsipaddress>[:awsport[:mystring]] Change the settings.");
po::variables_map vm;
store(po::parse_command_line(argc, argv, desc), vm);
notify(vm);
auto ep = Endpoint::parse_endpoint(setclient);
std::cout << std::left << "\t"
<< "Ip address: " << std::setw(12) << ep.address_ //
<< "Port: " << std::setw(12) << ep.port_ //
<< "Name: " << std::setw(12) << ep.name_ //
<< "\n";
}
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
namespace x3 = boost::spirit::x3;
BOOST_FUSION_ADAPT_STRUCT(Endpoint, address_, port_, name_)
Endpoint Endpoint::parse_endpoint(std::string_view text) { //
auto to_address = [](auto const& ctx) {
auto& r = _attr(ctx);
_val(ctx) = address::from_string({r.begin(), r.end()});
};
auto octet = x3::uint8;
auto ipv46 = x3::rule<void, address>{} = x3::raw[+~x3::char_("]")][to_address];
auto ipv4 = x3::rule<void, address>{} =
x3::raw[octet >> '.' >> octet >> '.' >> octet >> '.' >> octet][to_address];
auto port = (':' >> x3::uint16) | x3::attr(443);
auto name = (':' >> +x3::char_) | x3::attr(std::string("ui"));
auto spec = ('[' >> ipv46 >> ']' >> port >> name | //
ipv4 >> port >> name) //
>> x3::eoi;
Endpoint ep;
parse(begin(text), end(text), x3::expect[spec], ep);
return ep;
}
Printing for various test inputs:
+ ./a.out --setclient 192.68.1.1
Ip address: 192.68.1.1 Port: 443 Name: ui
+ ./a.out --setclient 192.68.1.1:backend
Ip address: 192.68.1.1 Port: 443 Name: backend
+ ./a.out --setclient 192.68.1.1:8443
Ip address: 192.68.1.1 Port: 8443 Name: ui
+ ./a.out --setclient 192.68.1.1:8443:backend
Ip address: 192.68.1.1 Port: 8443 Name: backend
+ ./a.out --setclient '[::1]'
Ip address: ::1 Port: 443 Name: ui
+ ./a.out --setclient '[::1]:backend'
Ip address: ::1 Port: 443 Name: backend
+ ./a.out --setclient '[::1]:8443'
Ip address: ::1 Port: 8443 Name: ui
+ ./a.out --setclient '[::1]:8443:backend'
Ip address: ::1 Port: 8443 Name: backend
+ ./a.out --setclient 127.0.0.1
Ip address: 127.0.0.1 Port: 443 Name: ui
+ ./a.out --setclient 127.0.0.1:backend
Ip address: 127.0.0.1 Port: 443 Name: backend
+ ./a.out --setclient 127.0.0.1:8443
Ip address: 127.0.0.1 Port: 8443 Name: ui
+ ./a.out --setclient 127.0.0.1:8443:backend
Ip address: 127.0.0.1 Port: 8443 Name: backend
struct Endpoint {
address address_;
uint16_t port_;
std::string name_;
static Endpoint parse_endpoint(std::string_view);
friend std::ostream& operator<<(std::ostream& os, Endpoint const& ep) {
return os << "[" << ep.address_ << "]:" << ep.port_ << ":" << ep.name_;
}
friend void validate(boost::any& value, std::vector<std::string> const& tt, Endpoint*, int) {
if (tt.size() != 1)
throw po::invalid_option_value("too many tokens");
value = parse_endpoint(tt.front());
}
};
int main(int argc, char** argv) {
po::options_description desc;
Endpoint ep{boost::asio::ip::address_v4::loopback(), 443, "ui"};
desc.add_options() //
("setclient", po::value<Endpoint>(&ep),
"<awsipaddress>[:awsport[:mystring]] Change the settings.");
po::variables_map vm;
store(po::parse_command_line(argc, argv, desc), vm);
notify(vm);
std::cout << "\tparsed: " << ep << "\n";
}