This question is a continuation of my previous question.
I am working on a project which uses the LLVM YAML I/O library. This is the documentation/tutorial that I am following:
I have created a small program that would read in a yaml file into objects in memory. Then it would print those objects. Finally it would write the objects out into a different output file.
First it parses the command line arguments. Then it creates the input reader
. Then it creates the YAML Input
object, which is used to read the *.yaml
file in, and parse it. It is in the parsing step that I am having an error. Supposing if parsing the input from the *.yaml
file was successful, the data will get stored into the DocType myDoc;
. Then it prints all the Person
objects stored in that std::vector
. An overloaded operator<<()
prints each element
. Then it creates the output writer
, creates the YAML Output
, and writes myDoc
into the file output_file.yaml
.
The goal of this program is to demonstrate reading and writing a *.yaml
file with LLVM YAML I/O. It successfully writes the output file, but it cannot read the input file.
Now suppose that instead of filling myDoc
with elements from the yin
, I would be manually adding elements instead. So I activate the code that push_back
each element, and I disable the code that reads the input from the yin
into memory.
DocType myDoc;
///*
myDoc.push_back(Person("Tom", 8));
myDoc.push_back(Person("Dan", 7));
myDoc.push_back(Person("Ken"));
//*/
/* Reading input into the memory */
/*
yin >> myDoc;
if (error_code errc = yin.error()) {
errs() << "error parsing YAML input from file " << InputFile << '\n';
errs() << errc.message() << '\n';
return EXIT_FAILURE;
} else {
outs() << "parsing YAML input from file " << InputFile << '\n';
}
*/
In that case, the programs works fine. The myDoc
is initialized with those elements, and then it prints each element to the stdout
. Then in creates the output writer, creates the YAML Output
, and writes the myDoc
into the output_file.yaml
.
Here is what the output file looks like when it is written:
---
- name: Tom
hat-size: 8
- name: Dan
hat-size: 7
- name: Ken
...
I copy the output file into the input file, for testing the input functionality of the program.
cp output_file.yaml input_file.yaml
Then I deactivate the code which manually fills the myDoc
, and I activate the code which fills the myDoc
from the yin
.
DocType myDoc;
/*
myDoc.push_back(Person("Tom", 8));
myDoc.push_back(Person("Dan", 7));
myDoc.push_back(Person("Ken"));
*/
/* Reading input into the memory */
///*
yin >> myDoc;
if (error_code errc = yin.error()) {
errs() << "error parsing YAML input from file " << InputFile << '\n';
errs() << errc.message() << '\n';
return EXIT_FAILURE;
} else {
outs() << "parsing YAML input from file " << InputFile << '\n';
}
//*/
After that the code no longer works. If I try to provide that same input_file.yaml
to the application, I get a bug. LLVM YAML I/O fails to parse the *.yaml
file and prints an error! It's weird because this is the exact format that this same LLVM YAML I/O was outputting into the file.
./yaml_project --input-file=input_file2.yaml --output-file=output_file.yaml
opening input file input_file2.yaml
reading input file input_file2.yaml
input_file2.yaml:1:1: error: not a sequence
-
^
error parsing YAML input from file input_file2.yaml
Invalid argument
I cannot find why is it refusing to accept well formatted YAML code from the input file. If anyone knows how to fix this bug, please help me.
Here is the full listing of my code:
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/CommandLine.h"
#include <cstdlib> /* for EXIT_FAILURE */
#include <string> /* for std::string */
#include <vector> /* for std::vector */
#include <system_error> /* for std::error_code */
using std::string;
using std::vector;
using std::error_code;
using llvm::outs;
using llvm::errs;
using llvm::raw_ostream;
using llvm::raw_fd_ostream;
using llvm::ErrorOr;
using llvm::MemoryBuffer;
using llvm::yaml::ScalarEnumerationTraits;
using llvm::yaml::MappingTraits;
using llvm::yaml::IO;
using llvm::yaml::Input;
using llvm::yaml::Output;
using llvm::cl::opt;
using llvm::cl::desc;
using llvm::cl::ValueRequired;
using llvm::cl::OptionCategory;
using llvm::cl::ParseCommandLineOptions;
/* Command line options description: */
// Apply a custom category to all command-line options so that they are the
// only ones displayed.
// The category tells the CommonOptionsParser how to parse the argc and argv.
OptionCategory yamlCategory("yaml_project options");
opt<string> InputFile("input-file", desc("The input YAML file"), ValueRequired);
opt<string> OutputFile("output-file", desc("The output YAML file"), ValueRequired);
struct Person {
string name;
int hatSize;
Person(string name = "", int hatSize = 0)
: name(name), hatSize(hatSize) {}
};
raw_ostream& operator<<(raw_ostream& os, const Person& person) {
os << "{ " << person.name;
if (person.hatSize)
os << " , " << person.hatSize;
os << " }";
return os;
}
template <>
struct MappingTraits<Person> {
static void mapping(IO& io, Person& info) {
io.mapRequired("name", info.name);
io.mapOptional("hat-size", info.hatSize, 0);
}
};
typedef vector<Person> DocType;
LLVM_YAML_IS_SEQUENCE_VECTOR(Person)
int main(int argc, const char **argv) {
/* Command line parsing: */
ParseCommandLineOptions(argc, argv);
if (InputFile.empty()) {
errs() << "No input file specified\n";
return EXIT_FAILURE;
}
if (OutputFile.empty()) {
errs() << "No output file specified\n";
return EXIT_FAILURE;
}
/* Create the input reader */
auto reader = MemoryBuffer::getFile(InputFile, true);
if (error_code errc = reader.getError()) {
errs() << "error opening input file " << InputFile << '\n';
errs() << errc.message() << '\n';
// MemoryBuffer does not need to be closed
return EXIT_FAILURE;
} else {
outs() << "opening input file " << InputFile << '\n';
}
/* Create the YAML Input */
// dereference once to strip away the llvm::ErrorOr
// dereference twice to strip away the std::unique_ptr
Input yin(**reader);
if (error_code errc = yin.error()) {
errs() << "error reading input file " << InputFile << '\n';
outs() << errc.message() << '\n';
// MemoryBuffer does not need to be closed
return EXIT_FAILURE;
} else {
outs() << "reading input file " << InputFile << '\n';
}
DocType myDoc;
/*
myDoc.push_back(Person("Tom", 8));
myDoc.push_back(Person("Dan", 7));
myDoc.push_back(Person("Ken"));
*/
/* Reading input into the memory */
///*
yin >> myDoc;
if (error_code errc = yin.error()) {
errs() << "error parsing YAML input from file " << InputFile << '\n';
errs() << errc.message() << '\n';
return EXIT_FAILURE;
} else {
outs() << "parsing YAML input from file " << InputFile << '\n';
}
//*/
for (const Person& element : myDoc)
outs() << element << '\n';
/* Create the output writer */
error_code errc;
raw_fd_ostream writer(OutputFile, errc);
if (errc) {
errs() << "error opening output file " << OutputFile << '\n';
errs() << errc.message() << '\n';
writer.close();
return EXIT_FAILURE;
} else {
outs() << "opening output file " << OutputFile << '\n';
}
/* Create the YAML Output */
Output yout(writer);
/* Writing output into file */
yout << myDoc;
outs() << "writing YAML output into file " << OutputFile << '\n';
writer.close();
return EXIT_SUCCESS;
}