qrqma is a C++ implementaion of Jinja templates that tries to go as far as possible to reach Jinja's flexibility while retaining as much C++ mentality as possible. Please bear in mind that qrqma is not a 1to1 drop in replacement for Jinja since some of Jinja's features simply cannot be realized in C++. Also qrqma is still missing some features.
Most of this documentation is taken from the Jinja documentation. If you love Jinja but miss using it from C++, you might love qrqma as well.
The motivation behind qrqma is to bring the ease of dynamic content generation from Jinja (or django) to the world of C++.
Below is an analogous minimal template example to the one in the Jinja documentation.
The biggest difference to the Jinja example is that within a qrqma template you cannot access members of an object.
Therefore the functions href
and caption
need to be provided.
The reason for this is C++'s lack of runtime reflection.
qrqma is an attempt to get a template engine as far as possible in C++ (with type safety where it is possible and compiled templates for quick rendering!).
#include <iostream>
#include "qrqma/template.h"
int main() {
auto templateStr = R"(
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ href(item) }}">{{ caption(item) }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
{# a comment #}
</body>
</html>)";
struct NavItem { std::string href, caption; };
qrqma::Template rendering{
templateStr,
{
{"navigation", qrqma::symbol::List{{NavItem{"Home", "index.html"}, NavItem{"Blog", "blog.html"}}} },
{"href", qrqma::symbol::Function{[](NavItem const& ni) {
return ni.href;
}}},
{"caption", qrqma::symbol::Function{[](NavItem const& ni) {
return ni.caption;
}}},
{"a_variable", std::string{"qrqma is awesome!"}}
}
};
std::cout << rendering() << std::endl;
return 0;
}
More examples can be visited in the demo branch! Look for demo.cpp and qrqma_test.cpp.
- Filters
- tests (only the "is [test]" part); if statements are implemented
- Line statements
- Super blocks
- Macros (and Call)
- Loop-else blocks
- namespace objects
- inline if
To use qrqma in you C++ project the easiest way is to simply copy the qrqma sources into your project source directory and you're set.
Jinja allows for four types of statements, whereas qrqma only allows for three (line statements are not implemented)
{% ... %}
for (control) Statements{{ ... }}
for Expressions to print to the template output{# ... #}
for Comments not included in the template output
Template variables are passed to the template renderer or defined inside the template. There are two stages when you can pass variables to the renderer:
- during the Template's construction time
Variables passed this way will be considered constant; if the template compiler can deduce that a variable/expression substitution will yield a constant it will reduce the expression and possibly generate static-only output. This is what makes qrqma so fast! - during the Template's rendering time Variables passed here will be rendered the very same way as in Jinja. Except for variables that were already specified during compilation. Pre-rendered expressions/variables cannot be overridden during rendering time.
Attributes and member functions of variables cannot be (directly) accessed from within a qrqma template. However, a function to provide an accessor can easily be passed to the renderer.
Elements of variables of type symbols::Map
(which implies a map from std::string
s as keys to std::any
s) can be accessed like so:
{{ foo['bar'] }}
If a variable does not exist during compile time, expressions involving this variable will be evaluated during render-time.
If the variable is not defined during render time an exception will be thrown when the variable is used inside a calculation.
An undefined variable will render to the empty string when used in a {{}}
print-expression.
You can pass a symbols::Function
to the template and enable calls within the template to the passed function.
Like so:
auto templateStr = R"(
{% set my_list = make_list(5) -%}
<ul>
{%- for i in my_list %}
<li>{{ title(i) }}</li>
{%- endfor %}
</ul>
)";
struct ListElement {
std::string title;
};
qrqma::Template rendering{
content,
{
// to play with qrqma add symbols here
{"make_list", qrqma::symbol::Function{[](int numElements) {
qrqma::symbol::List l;
for (int i{}; i < numElements; ++i) {
l.emplace_back(ListElement{"title: " + std::to_string(i)});
}
return l;
}}},
{"title", qrqma::symbol::Function{[](ListElement const& le) {
return le.title;
}}},
}
};
std::cout << rendering() << std::endl;
This will generate the following output:
<ul>
<li>title: 0</li>
<li>title: 1</li>
<li>title: 2</li>
<li>title: 3</li>
<li>title: 4</li>
</ul>
Note that there is no member access operator within qrqma. Therefore, you need to pass a function that wraps this access.
In general, function calls are performed in a type-safe manner!
If the function you've supplied to the template has a single argument of type int
, it will only be invoked with an int
.
Qrqma tries to convert supplied types as good as possible but cannot do more automatic casts that C++ could.
If the template does something with a variable where no type conversion is known (e.g. pass a variable of type bool
to a function that expects a custom type) an exception will be thrown.
Also an exception will be thrown if an undefined function is called during render-time.
qrqma does not implement filters as Jinja does. However, filters can easily be created as simple function calls.
qrqma supports if
statements like the following:
{% if foo == 2 and True != False or "string" %}...{% else %}...{% endif %}
The else block is optional.
The semantics behind the if
-head are very similar to the semantics in python:
A nonempty string is considered True, a number different from 0 also, etc.
To comment out a part of the template simply wrap the comment in a {# comment #}
tag.
By default qrqma does not treat any whitespaces differently.
Everything that is part of the output, will be output.
However, you can use a single dash (-
) at the beginning or end of a control statement to suppress all whitespaces leading or trailing to the statement.
It's best demonstrated by an example:
<p>text</p>
{% if True %}
foobar
{% endif %}
<p>mote text</p>
will be rendered to:
<p>text</p>
foobar
<p>mote text</p>
With whitespace control you can suppress the output of the empty lines like so:
<p>text</p>
{% if True -%}
foobar
{%- endif -%}
<p>mote text</p>
will be rendered to:
<p>text</p>
foobar<p>mote text</p>
###Note: You must not add whitespace between the tag and the minus sign.
valid:
{%- if foo -%}...{% endif %}
invalid:
{% - if foo - %}...{% endif %}
It is sometimes desirable – even necessary – to have qrqma ignore parts it would otherwise handle as variables or blocks. For example, if, with the default syntax, you want to use {{ as a raw string in a template and not start a variable, you have to use a trick.
The easiest way to output a literal variable delimiter ({{) is by using a variable expression:
{{ '{{' }}
For bigger sections, it makes sense to mark a block raw. For example, to include example Jinja syntax in a template, you can use this snippet:
{% raw %}
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
The most powerfull feature of Jinja is inheritance. qrqma can do the same thing. The rest of this section is alsmost word-by-word taken from the Jinja documentation as the template inheritance methods are working very similarly. If you like jinja's template inheritance, you should feel right at home with qrqma.
Template inheritance allows you to build a base “skeleton” template that contains all the common elements of your site and defines blocks that child templates can override.
Sounds complicated but is very basic. It’s easiest to understand it by starting with an example.
This template, which we’ll call base.html, defines a simple HTML skeleton document that you might use for a simple two-column page. It’s the job of “child” templates to fill the empty blocks with content:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>
In this example, the {% block %} tags define four blocks that child templates can fill in. All the block tag does is tell the template engine that a child template may override those placeholders in the template.
A child template might look like this:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
The {% extends %}
tag is the key here. It tells the template engine that this template “extends” another template. When the template system evaluates this template, it first locates the parent. The extends tag should be the first tag in the template. Everything before it is printed out normally and may cause confusion. For details about this behavior and how to take advantage of it, see Null-Master Fallback. Also a block will always be filled in regardless of whether the surrounding condition is evaluated to be true or false.
The filename of the template depends on the template loader. For example, the defaultLoader
allows you to access other templates by giving the filename. You can access templates in subdirectories with a slash:
{% extends "layout/default.html" %}
But this behavior can depend on the application embedding qrqma. Note that since the child template doesn’t define the footer block, the value from the parent template is used instead.
You shouldn't define multiple {% block %}
tags with the same name in the same child template. This is discouraged because a block tag works in “both” directions. That is, a block tag doesn’t just provide a placeholder to fill - it also defines the content that fills the placeholder in the parent. If there were two same-named {% block %}
tags in a child template, that template’s parent wouldn’t know which one of the blocks’ content to use.
However, in base templates each {% block %}
will get the content of the child template's content.
qrqma allows you to put the name of the block after the end tag for better readability:
{% block sidebar %}
{% block inner_sidebar %}
...
{% endblock inner_sidebar %}
{% endblock sidebar %}
There is no functionality behind the end-tags. Technically you can write whatever you like in there.
Blocks can be nested for more complex layouts. However, blocks may not access variables from outer scopes:
base template:
{% for item in seq %}
<li>{% block loop_item %}{% endblock %}</li>
{% endfor %}
child template:
{% block loop_item %}{{ item }}{% endblock %}
The template parser would try to generate the output of the child template's loop_item
block but fail there as item
is not defined in the block's scope.
A control structure refers to all those things that control the flow of a program - conditionals (i.e. if/elif/else), for-loops, as well as things like macros and blocks. With the default syntax, control structures appear inside {% ... %}
blocks.
Loop over each item in a sequence. For example, to display a list of users provided in a variable called users:
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user }}</li>
{% endfor %}
</ul>
As variables in templates retain their object properties, it is possible to iterate over containers like dict.
Note that the pythonic call to .items()
is not necessary in qrqma.
Either the key-value-pairs are broadcasted into two variables or the key is broadcasted into the single loop variable
<dl>
{% for key, value in my_dict %}
<dt>{{ key }}</dt>
<dd>{{ value }}</dd>
{% endfor %}
</dl>
Or similarly using a single loop variable:
<dl>
{% for key in my_dict %}
<dt>{{ key }}</dt>
<dd>{{ my_dict[key] }}</dd>
{% endfor %}
</dl>
Note, however, that C++ std::map
s are ordered.
Inside of a for-loop block, you can access some special variables:
Variable | Description |
---|---|
loop.index | The current iteration of the loop. (1 indexed) |
loop.index0 | The current iteration of the loop. (0 indexed) |
loop.revindex | The number of iterations from the end of the loop (1 indexed) |
loop.revindex0 | The number of iterations from the end of the loop (0 indexed) |
loop.first | True if first iteration. |
loop.last | True if last iteration. |
loop.length | The number of items in the sequence. |
loop.depth | Indicates how deep in a recursive loop the rendering currently is. Starts at level 1 |
loop.depth0 | Indicates how deep in a recursive loop the rendering currently is. Starts at level 0 |
loop.previtem | The item from the previous iteration of the loop. Undefined during the first iteration. |
loop.nextitem | The item from the following iteration of the loop. Undefined during the last iteration. |
The loop variable always refers to the closest (innermost) loop.
If we have more than one level of loops, we can rebind the loop variables by writing {% set outer_loop.index = loop.index %}
after the loop that we want to use recursively.
Then, we can call it using {{ outer_loop.index }}
.
As qrqma does not support member access, variables are allowed to have a '.' in their names.
In the example the variable outer_loop.index
is a single variable with a slightly confusing name.
This was chosen to give a as close-to-Jinja experience as possible.
In contrast fo Jinja, variables declared within loops will also be visible outside of the loop's scope. This reflects python's behavior of scopes. Note that the special variables from within a loop will not be visible outside the scope.
The if statement in qrqma is comparable with the Jinja if statement. In the simplest form, you can use it to test if a variable is not empty and not false:
{% if users %}
<ul>
{% for user in users %}
<li>{{ user }}</li>
{% endfor %}
</ul>
{% endif %}
For multiple branches, elif and else can be used like in Python. You can use more complex Expressions there, too:
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
Inside control blocks, you can also assign values to variables. Assignments at top level that are specified before the {% extends ... %}
keyword are exported from the template and can be used within parent templates.
Assignments use the set tag:
{% set navigation_map = {'Index':'index.html', 'About':'about.html'} %}
{% set navigation_list = ['index.html', 'about.html'] %}
{% set my_variable = foobar() %}
Please keep in mind that the scoping behavior of qrqma is the same as in python. When a scope is exited the local variables will be promoted to the parent scope. qrqma will even recognize if a constant was promoted and will treat the variable after the scope as a constant trying to pre-render as much of the template as possible.
This behavior is different to Jinja's scoping behavior!
The extends tag can be used to extend one template from another. You can have multiple extends tags in a file, but only one of them may be executed at a time.
See the section about Template Inheritance above.
Blocks are used for inheritance and act as both placeholders and replacements at the same time. They are documented in detail in the Template Inheritance section.
qrqma allows basic expressions everywhere. These work very similarly to regular Python; even if you’re not working with Python you should feel comfortable with it.
The simplest form of expressions are literals. Literals are representations for objects such as strings and numbers. The following literals exist:
"Hello World"
:
Everything between two double or single quotes is a string. They are useful whenever you need a string in the template (e.g. as arguments to function calls and filters, or just to extend or include a template).42 / 42.23
:
Integers and floating point numbers are created by just writing the number down. If a dot or exponent is present, the number is a float, otherwise an integer. Keep in mind that, in qrqma, 42 and 42.0 are different (int and float, respectively).[‘list’, ‘of’, ‘objects’]
:
Everything between two brackets is a list. Lists are useful for storing sequential data to be iterated over. For example, you can easily create a list of links using lists for (and with) a for loop:<ul> {% for item in [['index.html', 'Index'], ['about.html', 'About'], ['downloads.html', 'Downloads']] %} <li><a href="{{ item[0] }}">{{ item[1] }}</a></li> {% endfor %} </ul>
{‘dict’: ‘of’, ‘key’: ‘and’, ‘value’: ‘pairs’}
:
A dict in qrqma is a structure that combines keys and values. Keys must be unique strings and always have exactly one value.true
/True
/false
/False
:
true is always True and false is always False. And vice versa.
qrqma allows you to calculate with values. This is rarely useful in templates but exists for completeness’ sake. The following operators are supported:
+
Adds two objects together. Usually the objects are numbers, but if both are strings or lists, you can concatenate them this way.{{ 1 + 1 }}
is 2.-
Subtract the second number from the first one.{{ 3 - 2 }}
is 1./
Divide two numbers. The return value will have the same type as a division in C++.{{ 1 / 2 }}
is{{ 0 }}
,{{ 1. / 2 }}
is{{ 0.5 }}
.%
Calculate the remainder of an integer division.{{ 11 % 7 }}
is 4.*
Multiply the left operand with the right one.{{ 2 * 2 }}
would return 4.
==
Compares two objects for equality.!=
Compares two objects for inequality.>
true if the left hand side is greater than the right hand side.>=
true if the left hand side is greater or equal to the right hand side.<
true if the left hand side is lower than the right hand side.<=
true if the left hand side is lower or equal to the right hand side.
For if statements it can be useful to combine multiple expressions:
and
/&&
Return true if the left and the right operand are true.or
/||
Return true if the left or the right operand are true.not
/!
negate a statement.(expr)
group an expression.
There are quite a lot of features missing in qrqma that Jinja has implemented. E.g., qrqma does not yet have builtins. Those features will come in the future, though!
However, qrqma is already a quite versatile and usable template engine!