【问题标题】:boost::program_options for single-byte variablesboost::program_options 用于单字节变量
【发布时间】:2013-01-17 23:26:45
【问题描述】:

*强调文本*如何使用 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] 不要使用类似 char 的值 [2] 手动解析(绕过 Boost)或 [3] 在 Boost 解析后执行二次转换。

【问题讨论】:

  • 哪个平台和工具链?
  • 我在 VC2012 的 Win7-64 上发现了这个,但这也适用于 Linux X86 和 Linux ARM 和 gcc。我认为这并不重要 - 这不是特定于平台的错误,而是流式传输的工作方式,并且任何解决方案都可以跨平台工作。
  • I tried your reproducer 并查看预期结果。我在 ppc64 Linux 和 Mac OS X 上看到了相同的行为。也许您可以详细说明您的环境?
  • 山姆:谢谢。您的代码重现了该问题,我使用您的代码示例编辑了我的帖子,并添加了强制转换以使问题更加明显。我在 Windows 和 Linux x86 上得到了相同的结果,这是由 boost::lexical_cast 引起的。顺便说一句,感谢有关 LiveWorkSpace 的提示。
  • 我猜这是因为uint8_t 可能是char 的typedef。也许将此作为​​错误提交?

标签: c++ boost boost-program-options


【解决方案1】:

创建一个字节大小的数字字节类,但像数值一样流式传输,而不是像 char 一样流式传输。

#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】:

    您观察到的行为与boost::program_optionsboost::lexical_cast 无关,这只是uint8_t 类型的流提取的工作原理。以下demonstrates那个

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

    这是sample session

    【讨论】:

    • 但是,id1 不再是单字节值了。
    • @Jim 正确,它是一个接受来自0-255的值的整数
    • Sam:我想我确实要求提供单字节值。很抱歉移动奶酪,但我将问题从“单字节值”更改为“单字节变量”。
    • @JimFred:好的,所以您的问题的答案是“停止使用单字节变量”。稍后将值复制到一个字节中真的那么难吗?或者只是使用更大的数据量?它不会伤害任何人;您的程序不会崩溃,因为您使用了 uint16_t 而不是 uint8_t
    • Nicol:通信数据包的大小很重要。正如您所说(并且如原始帖子中所述),可以进行二次转换或复制。我一直在寻找影响 Boost 转化的选项。
    猜你喜欢
    • 2021-08-08
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    • 2019-05-01
    • 2019-01-14
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多