Search code examples
c++streamboost-program-options

Why does parse_config_file set failbit on a stream?


This minimal program uses boost::program_options to parse a stringstream. Strangely, after parsing, the stream is not in a "good" state anymore and both failbit and eofbit are set.

#include <iostream>
#include <sstream>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>

void test_stream(std::stringstream& s);

int main()
{
  using namespace std;
  namespace po = boost::program_options;

  stringstream s;
  s << "seed=3" << '\n';
  test_stream(s);

  po::options_description desc("");
  desc.add_options()
    ("seed", po::value<int>());
  po::variables_map vm;
  po::store(po::parse_config_file(s, desc, true), vm);
  po::notify(vm);

  test_stream(s);

  return 0;
}

void test_stream(std::stringstream& s)
{
  using namespace std;

  if (s.good())
    {
      cout << "stream is good" << endl;
    }
  else
    {
      cout << "stream is not good" << endl;
      if (s.rdstate() & ios_base::badbit)
    cout << "badbit is set" << endl;
      if (s.rdstate() & ios_base::failbit)
    cout << "failbit is set" << endl;
      if (s.rdstate() & ios_base::eofbit)
    cout << "eofbit is set" << endl;
    }
}

Output:

stream is good
stream is not good
failbit is set
eofbit is set

Although the eof condition is somehow expected, since presumably the parser has read the stream until EOF, why is also the failbit set?


Solution

  • According to the documentation of the ios::eof flag this might happen in certain cases:

    Operations that attempt to read at the End-of-File fail, and thus both the eofbit and the failbit end up set. This function can be used to check whether the failure is due to reaching the End-of-File or to some other reason.

    Boost's parser uses std::copy() and an iterator on the stream to extract the options. As pointed out in Jerry Coffin's answer to another question, the iterator reads the items from the sequence. After reading the last one the sequence's eof bit is set. When the iterator is incremented another time to get the end-of-stream iterator, which is the condition to leave the loop in copy(), it tries to read again and therefor also the stream's fail bit is set.