Opened 6 years ago

Last modified 5 years ago

#12535 new Bugs

boost::program_options cannot handle std::uint8_t arguments

Reported by: gilgamash Owned by: Vladimir Prus
Milestone: To Be Determined Component: program_options
Version: Boost 1.61.0 Severity: Problem
Keywords: uint8_t Cc:

Description

When using std::uint8_t (and probably std::int8_t as well) as a value type for parameters, the store method crashes with an exception: the argument (value) for option '(option)' is invalid.

Attachments (1)

program_options_uint8_demo.cc (375 bytes ) - added by Kalle Olavi Niemitalo <kon@…> 5 years ago.
reproduce the error (Boost 1.55.0, GCC 4.9.2, Debian 8.8)

Download all attachments as: .zip

Change History (3)

comment:1 by Vladimir Prus, 6 years ago

Please provide a complete minimal example that shows this problem. Thanks!

by Kalle Olavi Niemitalo <kon@…>, 5 years ago

reproduce the error (Boost 1.55.0, GCC 4.9.2, Debian 8.8)

comment:2 by Kalle Olavi Niemitalo <kon@…>, 5 years ago

Compile with:

c++ -std=c++11 -Wall -Wextra -Wold-style-cast -pedantic -ggdb program_options_uint8_demo.cc -lboost_program_options -o program_options_uint8_demo

Run with:

./program_options_uint8_demo

Actual result:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::invalid_option_value> >'
  what():  the argument ('241') for option '--num' is invalid

Backtrace when the exception is being thrown:

  1. in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
  2. in boost::throw_exception<boost::bad_lexical_cast> (e=...) at /usr/include/boost/throw_exception.hpp:70
  3. in boost::detail::lexical_cast_do_cast<unsigned char, std::string>::lexical_cast_impl (arg="241") at /usr/include/boost/lexical_cast.hpp:2375
  4. in boost::lexical_cast<unsigned char, std::string> (arg="241") at /usr/include/boost/lexical_cast.hpp:2543
  5. in boost::program_options::validate<unsigned char, char> (v=..., xs=std::vector of length 1, capacity 1 = {...}) at /usr/include/boost/program_options/detail/value_semantic.hpp:89
  6. in boost::program_options::typed_value<unsigned char, char>::xparse (this=0x619070, value_store=..., new_tokens=std::vector of length 1, capacity 1 = {...}) at /usr/include/boost/program_options/detail/value_semantic.hpp:170
  7. in boost::program_options::value_semantic_codecvt_helper<char>::parse(boost::any&, std::vector<std::string, std::allocator<std::string> > const&, bool) const () from /usr/lib/x86_64-linux-gnu/libboost_program_options.so.1.55.0
  8. in boost::program_options::store(boost::program_options::basic_parsed_options<char> const&, boost::program_options::variables_map&, bool) () from /usr/lib/x86_64-linux-gnu/libboost_program_options.so.1.55.0
  9. in main () at program_options_uint8_demo.cc:16

Cause:

lexical_cast sees uint8_t as unsigned char and expects a one-character string, but "241" has three characters. Likewise, --num=9 would set num='9', i.e. num=57 in ASCII, not num=9.

Rationale:

I expect value<char> to parse a one-character argument; if that were changed to parse an integer, it would be too surprising.

OTOH, options that require a one-character argument and save that as char don't seem very useful nowadays, because of multibyte characters.

If value<char> parses a one-character argument, and int8_t is defined as char (rather than signed char), then value<int8_t> is undistinguishable from value<char> and must likewise parse a one-character argument.

If value<char> parsed a one-character argument but value<unsigned char> were changed to parse an integer, that would be an annoying asymmetry.

Passing arbitrary raw bytes as unsigned char in the command line is not feasible because argv[] does not support null bytes and Windows always requires a conversion from UTF-16.

Recommendation for programs:

Define the argument type as a string if you want a single character, and as int if you want an 8-bit integer. Then separately check that the string isn't too long or that the integer isn't out of range.

Recommendation for Boost:

Keep value<char> and value<unsigned char> parsing a single-character argument like now, but change the error message to say that the argument is too long and a single character was expected. This will make it easier for the programmer to understand how to fix the problem if integer parsing was intended.

Perhaps add separate integer_value functions that support uint8_t and allow the programmer to specify the permitted range of integers.

Note: See TracTickets for help on using tickets.