patterncppMinor
Simple C++ wrapper over libYAML
Viewed 0 times
wrappersimpleoverlibyaml
Problem
For a personal project, I need to parse YAML file. I chose to use C-based libYAML instead of yaml-cpp, because:
I wrote this lightweight wrapper around the library.
The wrapper aims at encapsulating all the details of the C library, throwing exceptions when errors arise and leaking no resources in the process. In addition, its header must not have any libYAML-related stuff in it, so I can change the underlying implementation later should I want to.
Provided API is an inheritable class that converts parsing events into virtual method calls, a la hierarchical visitor pattern, but without conditional navigation.
Here comes the API (YAMLParser.h):
```
#ifndef YAML_PARSER_H
#define YAML_PARSER_H
#include
#include
#include
#include
class YAMLParser
{
public:
class ParseError : public std::exception
{
public:
ParseError(const std::string & what, size_t line, size_t col);
ParseError(const std::string & what, size_t line, size_t col,
const std::string & context, size_t ctx_line);
const char *what() const noexcept override { return m_what.c_str(); }
protected:
std::string m_what;
};
public:
virtual ~YAMLParser() {}
virtual void parse(std::istream & stream);
protected:
virtual void streamStart() = 0;
virtual void streamEnd() = 0;
virtual void documentStart() = 0;
virtual void documentEnd() = 0;
virtual void sequenceStart(const std::string & tag, const std::string & anchor) = 0;
virtual void sequenceEnd() = 0;
virtual void mappingStart(const std::string & tag, const std::string & anchor) = 0;
virtual void
- I try to keep my dependencies to what's commonly installed and Debian statistics show 45% installs already have libYAML, vs 0.5% for yaml-cpp.
- I don't need variant node types, as the config parser that uses this wrapper builds the final datastructures directly using a state machine.
I wrote this lightweight wrapper around the library.
The wrapper aims at encapsulating all the details of the C library, throwing exceptions when errors arise and leaking no resources in the process. In addition, its header must not have any libYAML-related stuff in it, so I can change the underlying implementation later should I want to.
Provided API is an inheritable class that converts parsing events into virtual method calls, a la hierarchical visitor pattern, but without conditional navigation.
Here comes the API (YAMLParser.h):
```
#ifndef YAML_PARSER_H
#define YAML_PARSER_H
#include
#include
#include
#include
class YAMLParser
{
public:
class ParseError : public std::exception
{
public:
ParseError(const std::string & what, size_t line, size_t col);
ParseError(const std::string & what, size_t line, size_t col,
const std::string & context, size_t ctx_line);
const char *what() const noexcept override { return m_what.c_str(); }
protected:
std::string m_what;
};
public:
virtual ~YAMLParser() {}
virtual void parse(std::istream & stream);
protected:
virtual void streamStart() = 0;
virtual void streamEnd() = 0;
virtual void documentStart() = 0;
virtual void documentEnd() = 0;
virtual void sequenceStart(const std::string & tag, const std::string & anchor) = 0;
virtual void sequenceEnd() = 0;
virtual void mappingStart(const std::string & tag, const std::string & anchor) = 0;
virtual void
Solution
Exceptions
There is no need to write your own
The class
There is no need to write your own
what() method.The class
std::exception and std::runtime_error both accept a string in the constructor that defines the error message returned by what(). So there is no need to define your own version of this method:class ParseError : public std::runtime_error
{
public:
ParseError(const std::string & what, size_t line, size_t col,
const std::string & context = "", size_t ctx_line = -1)
: std::runtime_error(genErrMsg(what, line, col, context, ctx_line))
{}
private:
std::string static genErrMsg(const std::string & what, size_t line, size_t col,
const std::string & context, size_t ctx_line)
{
std::ostringstream error;
error << what
<< " line " << line + 1 << " column " << col + 1
<< " " << context;
if (ctx_line != -1) {
error << " from line " << ctx_line + 1;
}
return error.str();
}
};Code Snippets
class ParseError : public std::runtime_error
{
public:
ParseError(const std::string & what, size_t line, size_t col,
const std::string & context = "", size_t ctx_line = -1)
: std::runtime_error(genErrMsg(what, line, col, context, ctx_line))
{}
private:
std::string static genErrMsg(const std::string & what, size_t line, size_t col,
const std::string & context, size_t ctx_line)
{
std::ostringstream error;
error << what
<< " line " << line + 1 << " column " << col + 1
<< " " << context;
if (ctx_line != -1) {
error << " from line " << ctx_line + 1;
}
return error.str();
}
};Context
StackExchange Code Review Q#160762, answer score: 2
Revisions (0)
No revisions yet.