*emphasized text*How can I use Boost program options to accept single-byte variables from the command line?
Command line parameters of --id1=1 --id2=1
results in values of id1=49 (or '1', 0x31) and id2=1.
#include <stdint.h>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
namespace po = boost::program_options;
const int myargc = 3;
const char* myargv[] = {"foo","--id1=1","--id2=2" };
uint8_t id1;
uint16_t id2; // works as expected.
po::variables_map vm;
po::options_description cmd_options( "Command options" );
cmd_options.add_options()
( "id1", po::value<uint8_t >( &id1 )->default_value( 0 ), "A 1-byte ID" )
( "id2", po::value<uint16_t>( &id2 )->default_value( 0 ), "A 2-byte ID" )
;
po::store( po::parse_command_line( myargc, myargv, cmd_options ), vm );
po::notify( vm );
// Using command line parameters of --id1=1 --id2=1,
// at this point, id1=49 (or '1', 0x31) and id2=1.
cout << "BPO parsing of " << myargv[1] << " and " << myargv[2] << endl;
cout << "id1: " << id1 << endl;
cout << "id1: " << (int)id1 << endl;
cout << "id2: " << id2 << endl;
id1 = boost::lexical_cast<uint8_t>("1");
id2 = boost::lexical_cast<int>("2");
cout << "Using boost::lexical_cast" << endl;
cout << "id1: " << id1 << endl;
cout << "id1: " << (int)id1 << endl;
cout << "id2: " << id2 << endl;
}
output is:
BPO parsing of --id1=1 and --id2=2
id1: 1
id1: 49
id2: 2
Using boost::lexical_cast
id1: 1
id1: 49
id2: 2
Boost eventually calls boost::lexical_cast("1")' which converts as a char rather than a numeric value - a "1" becomes a '1' which is 49.
Is there a way to change the boost::program_options::add_options() initialization to treat single-bye values as ints rather than string/char? If not, what options do I have to change the parsing or mapping? Obvious (but unfavorable) options are: [1] don't use char-like values [2] manually parse (bypass Boost) or [3] perform a secondary conversion after Boost does its parsing.
The behavior you observe has nothing to do with boost::program_options
or boost::lexical_cast
, it's just how stream extraction for uint8_t
types works. The following demonstrates that
#include <iostream>
#include <sstream>
int
main()
{
uint8_t id1;
uint16_t id2;
std::istringstream iss( "1 1" );
iss >> id1 >> id2;
std::cout << "id1 char: " << id1 << std::endl;
std::cout << "id1 uint8_t: " << (uint8_t)id1 << std::endl;
std::cout << "id1 int: " << (int)id1 << std::endl;
std::cout << "id2 uint16_t: " << (uint16_t)id2 << std::endl;
std::cout << "id2 int: " << (int)id2 << std::endl;
}
produces:
id1 char: 1
id1 uint8_t: 1
id1 int: 49
id2 uint16_t: 1
id2 int: 1
If you want to limit your program options to a single byte, I suggest using boost::numeric_cast
#include <stdint.h>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/program_options.hpp>
int
main(int argc, char** argv)
{
(void)argc;
(void)argv;
namespace po = boost::program_options;
const int myargc = 3;
const char* myargv[] = {"foo","--id1=1","--id2=2" };
unsigned id1;
uint16_t id2; // works as expected.
po::options_description cmd_options( "Command options" );
cmd_options.add_options()
( "id1", po::value<unsigned>( &id1 )->notifier([](unsigned value){ boost::numeric_cast<uint8_t>(value); } ), "A 1-byte ID" )
( "id2", po::value<uint16_t>( &id2 ), "A 2-byte ID" )
;
po::variables_map vm;
po::store( po::parse_command_line( myargc, myargv, cmd_options ), vm );
po::notify( vm );
std::cout << "id1: " << id1 << std::endl;
std::cout << "id2: " << id2 << std::endl;
}
here is a sample session
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With