Simple Template



Project Status:


Even though this project was used in production within another project, I don't consider it production ready.


Motivation:


The goal of this simple template engine is to offer basic, easy to use functionality without the overhead and additional dependencies compared to established projects. Although it originated with the focus on HTML, it can be used also in other textual contexts.

Available functionalities are:
  • Variables that can be replaced by strings.
  • Loops with an loop variable and a counter.
  • if/else and switch to decide which of the provided content is used

All these elements can be used nested with exception of the variable.
The parsing is using regular expression and therefor the performance and the compatibility of the project is dependent on the regex STL implementation. Boosts regex implementation should offer better performance but since one of the reasons of the project was to avoid additional dependencies, I decided for the STL implementation.
In regard of input and output of the template and the filled HTML the engine offers direct file in- and output or the use of strings. If there is a parsing error this element will remain in the filled template. The only exception to this is a switch element with a default case defined and the error would be that no case to use is provided by the user. Than the default case is used.
I explain the usage of this simple template engine in this document using examples. The resulting template file as well as the resulted filled output file is found at the end of the document.


Usage:

The library can handle text/html files directly as well as strings as template. Indentation are important to differentiate child elements and content, especially with nested elements. Loops, if/else and switch elements need to be in their own lines, while variables can be placed everywhere except as names of elements.

Elements are build up as followed:

  1. an opening character sequence
  2. a sequence identification name for accessing the settings for this element
  3. (a separator character sequence in case of if/else and switch)
  4. (an accessor in case of loops)
  5. the content of the element
  6. a closing character sequence

There will be always two parts within the examples of the elements. The first part is the one from the template document, while the second one is the C++ code part for this element.

Variables:

Variables are identified using {{variablename}}. They can be replaced with any text content. If you want to use it to add additional template elements the template has to be parsed twice.
						
							The quick brown {{animal}} jumps over the lazy {{animal2}}.
						
					
To replace variables create a dictionary:
						
							std::map<std::string, std::string=""> vars;
							vars["animal"]  = "fox";
							vars["animal2"] = "dog";
						
					


Loops:

In this example we create a list of pangrams. The length of the list is defined in the corresponding C++ part as a vector.
In the template file, inside of the loop, there are two variables available for accessing the loop counter (##counterX##) and the value of the vector (##valueX##) at the counter. The X hereby stands for the number of the loop nestings, beginning at 0. This enables in an inner loop to access the current value of the outer one.
In the C++ part we again use a dictionary, this time with a vector of strings. The vector length is responsible how often the element content will be repeated, while the elements of the vector are used to replace the loop accessor variables.
						
							**pangramlist
##counter0##: ##value0##
**/
						
							std::map<std::string, std::vector <std::string=""> > loop;
							loop["pangramlist"] = std::vector();
							loop["pangramlist"].push_back("Sphinx of black quartz judge my vow.");
							loop["pangramlist"].push_back("A quick movement of the enemy will jeopardize six gunboats");
							loop["pangramlist"].push_back("Five quacking Zephyrs jolt my wax bed.");
							loop["pangramlist"].push_back("Heavy boxes perform waltzes and jigs.");
						
					


If/Else:


If/Else for two different options of content: Hereby the user just defines "true" to use the first content, while at "false" the second one is used. For example the answer if "The quick brown fox jumps over the lazy dog" is a pangram should be "true".
						
							<<isPangram
									Yes, it is.
								||
No, it is not a pangram. <</
						
							std::map<std::string, bool> ifElse;
							ifElse["isPangram"] = true;
						   
					


Switch:

Switch can be used for multiple options of different content - in the C++ part is defined which switch case will be used. In case the parser does not find a corresponding dictionary entry the default case will be used.
						
							>>pangram
||first
The five boxing wizards jump quickly.
||second
A quick movement of the enemy will jeopardize six gunboats.
||third
Sphinx of black quartz judge my vow.
||fourth
Five quacking Zephyrs jolt my wax bed.
||default
This is the default case which is chosen when nothing was set.
>>/
						
							std::map<std::string, std::string=""> swith;
							swith["pangram"] = "first";
						     
					


Nesting:


Let's print every pangram three times, with one blank line in between. To do this we use the already before used vector "pangram" from the loop example. To repeat it three times we use a vector containing three elements - which elements does not matter since we do not use them (they would be access with ##value1##, 1 for the inner loop). In this case, the outer loop contains the pangrams, while the inner loop just repeats the current loop pangram three times.
						
							**pangramlist

								**empty
									##value0##
								**/

								<br>
							**/
						
					
						
							loop["empty"] = std::vector<std::string>();
							loop["empty"].push_back("");
							loop["empty"].push_back("");
							loop["empty"].push_back("");
						
					


File In/Output:

Since now we have a template file and the C++ settings for the engine now we need to do the final setup, and specify file input and out.
						
							SimpleTemp::Template html("pangrams.tmpl", true);
							html.SetIfElseDictionary(ifElse);
							html.SetLoopDictionary(loop);
							html.SetSwitchDictionary(swith);
							html.SetVariableDictionary(vars);
							html.CreateFile("pangrams.html"); 
						
                    


The Simple Template Engine throws on errors occuring string exceptions.

Additional Resources:

You might need to use right click and save as...
The pangrams template file.
The output HTML file.
The main.cpp for this example.


Ideas for Future Work:

  • Use more accessor vectors for a loop (pangram & pangramcounter[first, second, third..].
  • Support for boost::regex.