std::optional
может помочь вам, но ему нужна стандартная библиотека, готовая к C ++ 17, поэтому вы можете также использовать boost::optional
, что более или менее одинаково, поскольку дизайн AFAIK std::optional
основан на буст один. (Поскольку повышение часто является источником новых стандартных предложений C ++)
Даже несмотря на то, что я не хочу делать вам предложение из-за общей проблемы вашего подхода, я все же написал для вас одно, но, пожалуйста, примите во внимание пункты после кода:
#include <string>
#include <regex>
#include <map>
#include <boost/optional.hpp>
class Tag {
static const std::regex re_get_name, re_get_attributes;
using string = std::string;
string _name;
std::map<string,string> _attr;
std::map<string,Tag> _children;
public:
Tag(const string &toParse) {
/* Parse line using the regex */
}
const string& name() const {
return _name;
}
Tag& add_child(const Tag& child) {
_children.emplace(child._name, child);
return *this;
}
boost::optional<Tag> get_child(const string& name) const {
auto val = _children.find(name);
return val == _children.cend() ? boost::optional<Tag>{} : boost::optional<Tag>{val->second};
}
boost::optional<string> attr(const string& name) const {
auto val = _attr.find(name);
return val == _attr.cend() ? boost::optional<string>{} : boost::optional<string>{val->second};
}
};
Как вы можете видеть, вы в основном просто реализуете семантику контейнера std::map
, но также и с помощью встроенной логики синтаксического анализатора. Я категорически не согласен с этим подходом, поскольку разбор становится очень уродливым в спешке, и смешивание кода генерации значений в контейнере, который можно использовать в качестве класса значений, еще больше ухудшит ситуацию.
Мое первое предложение состоит в том, чтобы просто объявить / использовать ваш Tag
class / struct в качестве класса значений, поэтому просто содержите std :: maps в качестве открытых членов. Поместите ваши функции синтаксического анализа в пространство имен вместе с контейнером Tag, и пусть они будут просто функциями или отдельными классами, если это необходимо.
Мое второе предложение небольшое: не используйте префикс _
, оно зарезервировано и считается плохим стилем, но вы можете использовать его в качестве суффикса. Также не используйте директивы пространства имен вне блока класса / функции / пространства имен, то есть глобально, это плохой стиль в .cpp и очень плохой стиль в заголовке /.h/.hpp
Мое третье предложение: используйте фреймворк qi парсера Boost Spirit, вы сначала просто объявите свои классы значений, как я предлагаю, в то время как Qi автоматически заполнит их, используя Boost Fusion. Если вы уже знаете нотацию EBNF, вы можете просто написать EBNF как грамматику в C ++, и компилятор сгенерирует синтаксический анализатор с помощью шаблона. Однако у ци и особенно слияния есть некоторые проблемы, но в долгосрочной перспективе это значительно облегчает ситуацию. В лучшем случае регулярные выражения выполняют только половину логики синтаксического анализа.