Mercurial > code
changeset 203:1ffe6d4937b7
Update parser for more convenience and types security #224
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 23 Jan 2014 14:56:50 +0100 |
parents | 99d0887395cc |
children | 7086e93bc4ea 706f861c4c6d |
files | C++/Parser.cpp C++/Parser.h |
diffstat | 2 files changed, 207 insertions(+), 265 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Parser.cpp Sat Jan 04 18:02:06 2014 +0100 +++ b/C++/Parser.cpp Thu Jan 23 14:56:50 2014 +0100 @@ -16,22 +16,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <cstring> +#include <cerrno> #include <iostream> #include <fstream> #include "Parser.h" /* -------------------------------------------------------- - * Option public members - * -------------------------------------------------------- */ - -bool operator==(const Option &o1, const Option &o2) -{ - return o1.m_key == o2.m_key && - o1.m_value == o2.m_value; -} - -/* -------------------------------------------------------- * Section public members * -------------------------------------------------------- */ @@ -40,108 +32,50 @@ { } +Section::Section(const std::string &name) + : m_name(name) + , m_allowed(true) +{ + +} + const std::string &Section::getName() const { return m_name; } -const std::string Section::findOption(const std::string &name) const +bool Section::hasOption(const std::string &name) const { - std::string ret; - - for (const Option &o : m_options) - if (o.m_key == name) { - ret = o.m_value; - break; - } - - return ret; + return m_options.count(name) >= 1; } -template <> -bool Section::getValue(const std::string &name) const +Section::Map::iterator Section::begin() { - bool result = false; - - if (hasOption(name)) { - std::string value = findOption(name); - - if (value == "yes" || value == "true"|| value == "1") - result = true; - else if (value == "no" || value == "false" || value == "0") - result = false; - } - - return result; + return m_options.begin(); } -template <> -int Section::getValue(const std::string &name) const +Section::Map::const_iterator Section::cbegin() const { - int result = -1; - - if (hasOption(name)) - result = atoi(findOption(name).c_str()); - - return result; + return m_options.cbegin(); } -template <> -std::string Section::getValue(const std::string &name) const +Section::Map::iterator Section::end() { - std::string result; - - if (hasOption(name)) - result = findOption(name); - - return result; -} - -const std::vector<Option> &Section::getOptions() const -{ - return m_options; + return m_options.end(); } -bool Section::hasOption(const std::string &name) const +Section::Map::const_iterator Section::cend() const { - for (const Option &o : m_options) - if (o.m_key == name) - return true; - - return false; -} - -bool operator==(const Section &s1, const Section &s2) -{ - if (s1.m_name != s2.m_name) - return false; - - return s1.m_options == s2.m_options; + return m_options.end(); } /* -------------------------------------------------------- * Parser private members * -------------------------------------------------------- */ -void Parser::addSection(const std::string &name) -{ - Section section; - - section.m_name = name; - section.m_allowed = true; - - m_sections.push_back(section); -} - void Parser::addOption(const std::string &key, const std::string &value) { - Option option; - Section ¤t = m_sections.back(); - - option.m_key = key; - option.m_value = value; - - current.m_options.push_back(option); + m_sections.back().m_options.insert(std::make_pair(key, value)); } void Parser::readSection(int lineno, const std::string &line) @@ -150,7 +84,7 @@ if ((end = line.find_first_of(']')) != std::string::npos) { if (end > 1) { - std::string name = line.substr(1, end - 1); + auto name = line.substr(1, end - 1); /* * Check if we can add a section, if redefinition is @@ -162,8 +96,9 @@ if (!(m_tuning & DisableVerbosity)) log(lineno, name, "redefinition not allowed"); m_sections.back().m_allowed = false; - } else - addSection(name); + } else { + m_sections.push_back(Section(name)); + } } else if (!(m_tuning & DisableVerbosity)) { /* * Do not add options at this step because it will @@ -177,9 +112,9 @@ void Parser::readOption(int lineno, const std::string &line) { + auto ¤t = m_sections.back(); size_t epos; std::string key, value; - Section ¤t = m_sections.back(); // Error on last section? if (!current.m_allowed) { @@ -267,6 +202,22 @@ const char Parser::DEFAULT_COMMENT_CHAR = '#'; +void Parser::open() +{ + std::ifstream file; + std::string line; + int lineno = 1; + + file.open(m_path.c_str()); + if (!file.is_open()) + throw std::runtime_error(m_path + std::string(std::strerror(errno))); + + while (std::getline(file, line)) + readLine(lineno++, line); + + file.close(); +} + Parser::Parser() { } @@ -276,60 +227,48 @@ , m_tuning(tuning) , m_commentChar(commentToken) { - Section root; + Section s(""); - // Add a default root section - root.m_name = ""; - root.m_allowed = (tuning & DisableRootSection) ? false : true; + s.m_allowed = (tuning & DisableRootSection) ? false : true; - m_sections.push_back(root); + m_sections.push_back(s); + open(); } Parser::~Parser() { } -bool Parser::open() +Parser::List::iterator Parser::begin() { - std::ifstream file; - std::string line; - int lineno = 1; - - file.open(m_path.c_str()); - if (!file.is_open()) { - m_error = "could not open file " + m_path; // XXX: add a real error - return false; - } - - // Avoid use of C getline - while (std::getline(file, line)) - readLine(lineno++, line); - - file.close(); - - return true; + return m_sections.begin(); } -const std::string &Parser::getError() const +Parser::List::const_iterator Parser::cbegin() const { - return m_error; + return m_sections.cbegin(); } -const std::vector<Section> &Parser::getSections() const +Parser::List::iterator Parser::end() { - return m_sections; + return m_sections.end(); +} + +Parser::List::const_iterator Parser::cend() const +{ + return m_sections.end(); } void Parser::findSections(const std::string &name, FindFunc func) const { - for (const Section &s : m_sections) + for (const auto &s : m_sections) if (s.m_name == name) func(s); } bool Parser::hasSection(const std::string &name) const { - for (const Section &s : m_sections) + for (const auto &s : m_sections) if (s.m_name == name) return true; @@ -338,34 +277,14 @@ const Section &Parser::getSection(const std::string &name) const { - for (const Section &s : m_sections) + for (const auto &s : m_sections) if (s.m_name == name) return s; - throw NotFoundException(name); + throw std::out_of_range(name + " not found"); } void Parser::log(int number, const std::string &, const std::string &message) { std::cout << "line " << number << ": " << message << std::endl; } - -void Parser::dump() -{ - for (auto s : m_sections) { - dumpSection(s); - - for (auto o : s.m_options) - dumpOption(o); - } -} - -void Parser::dumpSection(const Section §ion) -{ - std::cout << "Section " << section.m_name << std::endl; -} - -void Parser::dumpOption(const Option &option) -{ - std::cout << " Option " << option.m_key << " = " << option.m_value << std::endl; -}
--- a/C++/Parser.h Sat Jan 04 18:02:06 2014 +0100 +++ b/C++/Parser.h Thu Jan 23 14:56:50 2014 +0100 @@ -20,53 +20,14 @@ #define _PARSER_H_ #include <cstdlib> -#include <exception> #include <functional> -#include <iostream> +#include <stdexcept> #include <string> +#include <unordered_map> +#include <utility> #include <vector> /** - * @class NotFoundException - * @brief Exception raised when a section or option is not found - * - * Thrown when a section or an option is not found. - */ -class NotFoundException : public std::exception { -private: - std::string m_key; - -public: - NotFoundException(const std::string &key) - : m_key(key) - { - } - - const std::string & which() const - { - return m_key; - } - - virtual const char *what() const throw() - { - return "Property not found"; - } -}; - -/** - * @struct Option - * @brief A key-value pair - * - * An option referenced by a key and a value. - */ -struct Option { - std::string m_key; /*! option name */ - std::string m_value; /*! option value */ -}; - -bool operator==(const Option &o1, const Option &o2); - -/** * @class Section * @brief The option container * @@ -75,13 +36,22 @@ * section is "". */ class Section { -private: - const std::string findOption(const std::string &name) const; - public: - std::string m_name; /*! name of section */ - std::vector<Option> m_options; /*! list of options inside */ - bool m_allowed; /*! is authorized to push */ + using Map = std::unordered_map<std::string, std::string>; + + friend class Parser; + +private: + std::string m_name; /*! name of section */ + Map m_options; /*! list of options inside */ + bool m_allowed; /*! is authorized to push */ + + const std::string findOption(const std::string &name) const; +public: + template <typename T> + struct Converter { + static const bool supported = false; + }; /** * Default constructor. @@ -89,18 +59,11 @@ Section(); /** - * Get the section name + * Named constructor. * - * @return the section name + * @param name the section name */ - const std::string &getName() const; - - /** - * Get all options from that section. - * - * @return the list of options - */ - const std::vector<Option> &getOptions() const; + Section(const std::string &name); /** * Tells if that section has the specified option name. @@ -111,13 +74,57 @@ bool hasOption(const std::string &name) const; /** + * Get the section name + * + * @return the section name + */ + const std::string &getName() const; + + /** + * Return an iterator to the beginning. + * + * @return the iterator. + */ + Map::iterator begin(); + + /** + * Return a const iterator to the beginning. + * + * @return the iterator. + */ + Map::const_iterator cbegin() const; + + /** + * Return an iterator to the end. + * + * @return the iterator. + */ + Map::iterator end(); + + /** + * Return a const iterator to the end. + * + * @return the iterator. + */ + Map::const_iterator cend() const; + + /** * Template all functions for retrieving options value. * * @param name the option name * @return the value if found */ template <typename T> - T getValue(const std::string &name) const; + T getValue(const std::string &name) const + { + try { + return requireValue<T>(name); + } catch (...) { + // Catch any conversion error. + } + + return T(); + } /** * Requires an option, this works like getOption except @@ -126,29 +133,74 @@ * * @param name the name * @return the value - * @throw NotFoundException if not found + * @throw std::out_of_range if not found + * @throw std::invalid_argument on conversion failures */ template <typename T> T requireValue(const std::string &name) const { - if (!hasOption(name)) - throw NotFoundException(name); + static_assert(Converter<T>::supported, "invalid type requested"); - return getValue<T>(name); + return Converter<T>::convert(m_options.at(name)); } +}; + +template <> +struct Section::Converter<bool> { + static const bool supported = true; - friend std::ostream &operator<<(std::ostream & stream, const Section §ion) + static bool convert(const std::string &value) { - stream << "[" << section.getName() << "]" << std::endl; + bool result(false); - for (auto p : section.getOptions()) - stream << p.m_key << "=" << p.m_value << std::endl; + if (value == "yes" || value == "true"|| value == "1") + result = true; + else if (value == "no" || value == "false" || value == "0") + result = false; - return stream; + return result; } }; -bool operator==(const Section &s1, const Section &s2); +template <> +struct Section::Converter<int> { + static const bool supported = true; + + static int convert(const std::string &value) + { + return std::stoi(value); + } +}; + +template <> +struct Section::Converter<float> { + static const bool supported = true; + + static float convert(const std::string &value) + { + return std::stof(value); + } +}; + +template <> +struct Section::Converter<double> { + static const bool supported = true; + + static double convert(const std::string &value) + { + return std::stod(value); + } +}; + +template <> +struct Section::Converter<std::string> { + static const bool supported = true; + + static std::string convert(const std::string &value) + { + return value; + } +}; /** * @class Parser @@ -167,14 +219,14 @@ DisableVerbosity = 4 /*! be verbose by method */ }; - using FindFunc = std::function<void (const Section &)>; + using FindFunc = std::function<void (const Section &)>; + using List = std::vector<Section>; private: - std::vector<Section> m_sections; /*! list of sections found */ - std::string m_error; /*! if an error occured */ - std::string m_path; /*! path file */ - int m_tuning; /*! options for parsing */ - char m_commentChar; /*! the comment token default (#) */ + List m_sections; /*! list of sections found */ + std::string m_path; /*! path file */ + int m_tuning; /*! options for parsing */ + char m_commentChar; /*! the comment token default (#) */ void addSection(const std::string &name); void addOption(const std::string &key, const std::string &value); @@ -184,6 +236,8 @@ void readLine(int lineno, const std::string &line); + void open(); + public: static const char DEFAULT_COMMENT_CHAR; @@ -194,6 +248,7 @@ * @param path the file path * @param tuning optional tuning flags * @param commentToken an optional comment delimiter + * @throw std::runtime_error on errors * @see Tuning */ Parser(const std::string &path, int tuning = 0, char commentToken = Parser::DEFAULT_COMMENT_CHAR); @@ -209,25 +264,32 @@ virtual ~Parser(); /** - * Open the config file. + * Return an iterator to the beginning. * - * @return true on success + * @return the iterator. */ - bool open(); + List::iterator begin(); /** - * Get the error message if any + * Return a const iterator to the beginning. * - * @return the error message + * @return the iterator. */ - const std::string &getError() const; + List::const_iterator cbegin() const; /** - * Get all sections found + * Return an iterator to the end. * - * @return all sections + * @return the iterator. */ - const std::vector<Section> &getSections() const; + List::iterator end(); + + /** + * Return a const iterator to the end. + * + * @return the iterator. + */ + List::const_iterator cend() const; /** * Find all sections matching the name. @@ -250,7 +312,7 @@ * * @param name the section name * @return a section - * @throw NotFoundException if not found + * @throw std::out_of_range if not found */ const Section &getSection(const std::string &name) const; @@ -265,45 +327,6 @@ * @param message the message */ virtual void log(int number, const std::string §ion, const std::string &message); - - /** - * Dump all sections and options. - */ - void dump(); - - /** - * Dump function used in the dump() method. This default method - * only print the section name like: - * Section foo - * - * @param section the current section - * @see dump - */ - virtual void dumpSection(const Section §ion); - - /** - * Dump the option. The default method only print the option name - * and value. - * - * @param option the current option - * @see dump - */ - virtual void dumpOption(const Option &option); - - /** - * Write the configuration to the output stream. - * - * @param stream the output - * @param parser the configuration - * @return the stream - */ - friend std::ostream &operator<<(std::ostream &stream, const Parser &parser) - { - for (auto s : parser.m_sections) - stream << s; - - return stream; - } }; #endif // !_PARSER_H_