boost :: program_options для однобайтовых переменных

* выделенный текст * Как я могу использовать параметры программы Boost для принятия однобайтовых переменных из командной строки?

Параметры командной строки --id1=1 --id2=1 приводит к значениям id1 = 49 (или ‘1’, 0x31) и 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;

}

вывод:

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 в конечном счете вызывает boost :: lexical_cast («1») ‘, который преобразуется в виде символа, а не числового значения — «1» становится «1», равным 49.

Есть ли способ изменить инициализацию boost :: program_options :: add_options () для обработки однобайтовых значений как целых, а не как строка / символ? Если нет, какие варианты я должен изменить синтаксический анализ или отображение? Очевидные (но неблагоприятные) варианты: [1] не используют значения, подобные символу [2], анализируют вручную (обход Boost) или [3] выполняют вторичное преобразование после того, как Boost выполняет его анализ.

3

Решение

Поведение, которое вы наблюдаете, не имеет ничего общего с boost::program_options или же boost::lexical_castЭто просто, как поток извлечения для uint8_t виды работ. Следующие демонстрирует тот

#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;
}

производит:

id1 char:     1
id1 uint8_t:  1
id1 int:      49
id2 uint16_t: 1
id2 int:      1

Если вы хотите ограничить параметры вашей программы одним байтом, я предлагаю использовать 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;
}

вот образец сессии

3

Другие решения

Создайте числовой класс байтов, который является размером байта, но потоков, как числовое значение, а не потоков, как символ.

#include <stdint.h>
#include <iostream>
#include <boost/program_options.hpp>

using namespace std;

struct NumByte
{
uint8_t value;

NumByte() : value() {}
NumByte( const uint8_t &arg ) : value(arg) {}
NumByte( const NumByte &arg ) : value(arg.value) {}

operator uint8_t() const { return value; }

friend istream& operator>>(istream& in, NumByte& valArg)
{
int i;
in >> i;
valArg.value = static_cast<uint8_t>(i);
return in;
}

friend ostream& operator<<(ostream& out, NumByte& valArg)
{
out << static_cast<int>(valArg.value);
return out;
}
};

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" };

NumByte  id1;
uint16_t id2;

po::variables_map vm;
po::options_description cmd_options( "Command options" );
cmd_options.add_options()
( "id1", po::value<NumByte >( &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 );

assert( sizeof(NumByte)==1 ); // insure the size of a numeric byte is the size of a byte.

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<NumByte>("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;

}

вывод:

BPO parsing of --id1=1 and --id2=2
id1: 1
id1: 1
id2: 2
Using boost::lexical_cast
id1: 1
id1: 1
id2: 2
2