Why do we need virtual functions
Hi, Habr. If you know the answer to the question in the title, congratulations, you do not need this article. It is addressed to beginners in programming, like me, who can’t always independently understand all the intricacies of C ++ and other typed languages, and if they can, it is better to learn from other people's mistakes anyway.
In this article, I will not just answer the question " Why do we need virtual functions in C ++ ", but I will give an example from my practice. For a brief answer, you can turn to search engines that display something like the following: " Virtual functions are needed to provide polymorphism - one of the three OOP whales. Thanks to them, the machine itself can determine the type of object by pointer, without loading the programmer with this task"Okay, but the question" why "remains, although now it means a little different:" Why rely on the machine, spend extra time and memory, if you can podcast the pointer yourself, because the type of object to which it refers is almost always known ? “Indeed, casting at first glance leaves virtual functions without work, and it is it that causes errors and bad code. In small projects, the loss is invisible, but, as you will soon see, with the growth of the program, castes increase the listing in almost geometric progression.
First, let's recall where castes and virtual functions may be needed at all. A type is lost when an object declared with type A is allocated a new operation to allocate memory for an object of type B compatible with type A, usually inherited from A. Most often the object is not one, but an entire array. An array of pointers of the same type, each of which is waiting for the assignment of a memory area with objects of completely different types. Here is an example we will consider.
I won’t drag out for a long time, the task was this: based on a document marked up with Markedit hypertext markup language (you can read about it here), build a parsing tree and create a file containing the same document in the HTML markup. My solution consists of three sequential routines: parsing the source text into tokens, building a syntax tree from tokens and building an HTML document on its basis. We are interested in the second part.
The fact is that the nodes of the destination tree have different types (section, paragraph, text node, link, footnote, etc.), but for parent nodes, pointers to child nodes are stored in the array, and therefore have one type - Node.
In a simplified form itself parser works: creating a "root" trie tree with type Root , declared pointer open_node general type of the Node , which is then assigned an addresstree , and the type variable of the enumerated type Node_type , and then a cycle begins, iterating through the tokens from the very first to the last. At each iteration, the type of the open_node open node is entered into the type variable first (types in the form of an enumeration are stored in the node structure), followed by the switch statement , which checks the type of the next token (the types of tokens are already carefully provided by the lexer). In each branch of the switch, another branch is presented that checks the type variable, where, as we recall, contains the type of open node. Depending on its value, different actions are performed, for example: add a node-list of a certain type to an open node, open another node of a certain type in an open node and pass its address to open_node , close the open node, throw an exception. Applicable to the topic of the article, we are interested in the second example. Each open node (and generally every node that can be opened) already contains an array of pointers to nodes of type Node . Therefore, when we open a new node in an open node (we assign a memory region for an object of another type to the next array pointer), for a C ++ semantic analyzer it remains an instance of Node typewithout acquiring new fields and methods. A pointer to it is now assigned to the variable open_node , without losing the type of Node . But how to work with a pointer of a general Node type when you need to call a method, for example, a paragraph? For example, open_bold () , which opens a bold font node in it? After all, open_bold () is declared and defined as a method of the Paragraph class , and Node is completely unaware of it. In addition, open_node is also declared as a pointer to Node , and it must accept methods from all types of opening nodes.
There are two solutions here: the obvious and the right one. Obvious for a beginner isstatic_cast , and virtual functions are correct. Let's first look at one branch of the switch parser written using the first method:
Not bad. And now, I won’t drag it on for a long time, I will show the same section of code written using virtual functions:
The gain is obvious, but do we really need it? After all, then you have to declare in the Node class all methods of all derived classes as virtual and somehow implement them in each derived class. The answer is yes, indeed. There are not so many methods specifically in this program (29), and their implementation in derived classes that are not related to them consists of only one line: throw string ("error!"); . You can enable creative mode and come up with a unique line for each exception throw. But most importantly - due to code reduction, the number of errors in it has decreased. Casting is one of the most important causes of errors in code. Because after applying static_castthe compiler stops swearing if the class contains the called method. Meanwhile, different classes may contain different methods with the same name. In my case, 6 was hidden in the code !!! errors, while one of them was duplicated in several switch branches. Here she is:
Next, under the spoilers, I bring complete listings of the first and second versions of the parser.
In this article, I will not just answer the question " Why do we need virtual functions in C ++ ", but I will give an example from my practice. For a brief answer, you can turn to search engines that display something like the following: " Virtual functions are needed to provide polymorphism - one of the three OOP whales. Thanks to them, the machine itself can determine the type of object by pointer, without loading the programmer with this task"Okay, but the question" why "remains, although now it means a little different:" Why rely on the machine, spend extra time and memory, if you can podcast the pointer yourself, because the type of object to which it refers is almost always known ? “Indeed, casting at first glance leaves virtual functions without work, and it is it that causes errors and bad code. In small projects, the loss is invisible, but, as you will soon see, with the growth of the program, castes increase the listing in almost geometric progression.
First, let's recall where castes and virtual functions may be needed at all. A type is lost when an object declared with type A is allocated a new operation to allocate memory for an object of type B compatible with type A, usually inherited from A. Most often the object is not one, but an entire array. An array of pointers of the same type, each of which is waiting for the assignment of a memory area with objects of completely different types. Here is an example we will consider.
I won’t drag out for a long time, the task was this: based on a document marked up with Markedit hypertext markup language (you can read about it here), build a parsing tree and create a file containing the same document in the HTML markup. My solution consists of three sequential routines: parsing the source text into tokens, building a syntax tree from tokens and building an HTML document on its basis. We are interested in the second part.
The fact is that the nodes of the destination tree have different types (section, paragraph, text node, link, footnote, etc.), but for parent nodes, pointers to child nodes are stored in the array, and therefore have one type - Node.
In a simplified form itself parser works: creating a "root" trie tree with type Root , declared pointer open_node general type of the Node , which is then assigned an addresstree , and the type variable of the enumerated type Node_type , and then a cycle begins, iterating through the tokens from the very first to the last. At each iteration, the type of the open_node open node is entered into the type variable first (types in the form of an enumeration are stored in the node structure), followed by the switch statement , which checks the type of the next token (the types of tokens are already carefully provided by the lexer). In each branch of the switch, another branch is presented that checks the type variable, where, as we recall, contains the type of open node. Depending on its value, different actions are performed, for example: add a node-list of a certain type to an open node, open another node of a certain type in an open node and pass its address to open_node , close the open node, throw an exception. Applicable to the topic of the article, we are interested in the second example. Each open node (and generally every node that can be opened) already contains an array of pointers to nodes of type Node . Therefore, when we open a new node in an open node (we assign a memory region for an object of another type to the next array pointer), for a C ++ semantic analyzer it remains an instance of Node typewithout acquiring new fields and methods. A pointer to it is now assigned to the variable open_node , without losing the type of Node . But how to work with a pointer of a general Node type when you need to call a method, for example, a paragraph? For example, open_bold () , which opens a bold font node in it? After all, open_bold () is declared and defined as a method of the Paragraph class , and Node is completely unaware of it. In addition, open_node is also declared as a pointer to Node , and it must accept methods from all types of opening nodes.
There are two solutions here: the obvious and the right one. Obvious for a beginner isstatic_cast , and virtual functions are correct. Let's first look at one branch of the switch parser written using the first method:
case Lexer::BOLD_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_bold();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_bold();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_bold();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_bold();
else // INLINE
open_node = static_cast(open_node)->open_bold();
break; }
Not bad. And now, I won’t drag it on for a long time, I will show the same section of code written using virtual functions:
case Lexer::BOLD_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = open_node->open_paragraph();
open_node = open_node->open_bold();
} else if (type == Node::SECTION) {
open_node = open_node->open_paragraph();
open_node = open_node->open_bold();
} else if (type == Node::UNORDERED_LIST) {
open_node = open_node->close();
while (open_node->get_type() != Node::SECTION)
open_node = open_node->close();
open_node = open_node->open_paragraph();
open_node = open_node->open_bold();
} else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
open_node = open_node->open_bold();
break; }
The gain is obvious, but do we really need it? After all, then you have to declare in the Node class all methods of all derived classes as virtual and somehow implement them in each derived class. The answer is yes, indeed. There are not so many methods specifically in this program (29), and their implementation in derived classes that are not related to them consists of only one line: throw string ("error!"); . You can enable creative mode and come up with a unique line for each exception throw. But most importantly - due to code reduction, the number of errors in it has decreased. Casting is one of the most important causes of errors in code. Because after applying static_castthe compiler stops swearing if the class contains the called method. Meanwhile, different classes may contain different methods with the same name. In my case, 6 was hidden in the code !!! errors, while one of them was duplicated in several switch branches. Here she is:
else if (type == Node::
open_node = static_cast(open_node)->open_italic();
Next, under the spoilers, I bring complete listings of the first and second versions of the parser.
Parser with casting
Root * Parser::parse (const Lexer &lexer) {
Node * open_node(tree);
Node::Node_type type;
for (unsigned long i(0), len(lexer.count()); i < len; i++) {
type = open_node->get_type();
if (type == Node::CITE || type == Node::TEXT || type == Node::NEWLINE || type == Node::NOTIFICATION || type == Node::IMAGE)
throw string("error!");
switch (lexer[i].type) {
case Lexer::NEWLINE: {
if (type == Node::ROOT || type == Node::SECTION)
;
else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->add_text("\n");
else if (type == Node::TITLE)
open_node = static_cast(open_node)->add_text("\n");
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->add_text("\n");
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
} else if (type == Node::LINK) {
open_node = static_cast(open_node)->add_text(lexer[i].lexeme);
} else // INLINE
open_node = static_cast(open_node)->add_text(lexer[i].lexeme);
break; }
case Lexer::DOUBLE_NEWLINE: {
if (type == Node::ROOT || type == Node::SECTION)
;
else if (type == Node::PARAGRAPH) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
} else if (type == Node::QUOTE) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
} else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
} else
throw string("unexpected double newline!");
break; }
case Lexer::UNDERLINE: {
if (type == Node::ROOT)
open_node = tree->add_line();
else if (type == Node::SECTION) {
open_node = static_cast(open_node)->close();
open_node = tree->add_line();
} else if (type == Node::PARAGRAPH) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->close();
open_node = tree->add_line();
} else if (type == Node::TITLE)
throw string("unexpected underline inside title!");
else if (type == Node::QUOTE) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->close();
open_node = tree->add_line();
} else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->close();
open_node = tree->add_line();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->close();
open_node = tree->add_line();
} else // INLINE
throw string("unexpected underline inside inline span!");
break; }
case Lexer::TITLE_START: {
if (lexer[i].lexeme.size() > 7)
throw string("invalid title: \"" + lexer[i].lexeme + "\"!");
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_title(lexer[i].lexeme.size()-1);
} else if (type == Node::SECTION)
open_node = static_cast(open_node)->open_title(lexer[i].lexeme.size()-1);
else if (type == Node::PARAGRAPH) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_title(lexer[i].lexeme.size()-1);
} else if (type == Node::TITLE)
throw string("title can't contain another title!");
else if (type == Node::QUOTE) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_title(lexer[i].lexeme.size()-1);
} else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_title(lexer[i].lexeme.size()-1);
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_title(lexer[i].lexeme.size()-1);
} else if (type == Node::LINK)
throw string("link can't contain a title!");
else // INLINE
throw string("inline span can't contain a title!");
break; }
case Lexer::BOLD_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_bold();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_bold();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_bold();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_bold();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_bold();
else // INLINE
open_node = static_cast(open_node)->open_bold();
break; }
case Lexer::ITALIC_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_italic();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_italic();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_italic();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_italic();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_italic();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_italic();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_italic();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_italic();
else // INLINE
open_node = static_cast(open_node)->open_italic();
break; }
case Lexer::UNDERLINED_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_underlined();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_underlined();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_underlined();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_underlined();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_underlined();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_underlined();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_underlined();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_underlined();
else // INLINE
open_node = static_cast(open_node)->open_underlined();
break; }
case Lexer::OVERLINED_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_overlined();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_overlined();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_overlined();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_overlined();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_overlined();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_overlined();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_overlined();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_overlined();
else // INLINE
open_node = static_cast(open_node)->open_overlined();
break; }
case Lexer::THROWLINED_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_throwlined();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_throwlined();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_throwlined();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_throwlined();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_throwlined();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_throwlined();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_throwlined();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_throwlined();
else // INLINE
open_node = static_cast(open_node)->open_throwlined();
break; }
case Lexer::SUBSCRIPT_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_subscript();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_subscript();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_subscript();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_subscript();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_subscript();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_subscript();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_subscript();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_subscript();
else // INLINE
open_node = static_cast(open_node)->open_subscript();
break; }
case Lexer::SUPERSCRIPT_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_superscript();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_superscript();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_superscript();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_superscript();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_superscript();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_superscript();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_superscript();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_superscript();
else // INLINE
open_node = static_cast(open_node)->open_superscript();
break; }
case Lexer::MARKED_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_marked();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_marked();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_marked();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_marked();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_marked();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_marked();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_marked();
} else if (type == Node::LINK)
open_node = static_cast(open_node)->open_marked();
else // INLINE
open_node = static_cast(open_node)->open_marked();
break; }
case Lexer::MONOSPACE_START: {
if (type == Node::ROOT) {
open_node = tree->open_section();
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_monospace();
} else if (type == Node::SECTION) {
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_monospace();
} else if (type == Node::PARAGRAPH)
open_node = static_cast(open_node)->open_monospace();
else if (type == Node::TITLE)
open_node = static_cast(open_node)->open_monospace();
else if (type == Node::QUOTE)
open_node = static_cast(open_node)->open_monospace();
else if (type == Node::UNORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::PARAGRAPH)
open_node = static_cast(open_node)->close();
}
open_node = static_cast(open_node)->open_paragraph();
open_node = static_cast(open_node)->open_monospace();
} else if (type == Node::ORDERED_LIST) {
open_node = static_cast(open_node)->close();
while (open_node->get_type() != Node::SECTION) {
if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast(open_node)->close();
else if (open_node->get_type() == Node::UNORDERED_LIST)
open_node = static_cast