Skip to content

Commit a6961aa

Browse files
authored
Update README.md
1 parent ab149f4 commit a6961aa

File tree

1 file changed

+89
-70
lines changed

1 file changed

+89
-70
lines changed

README.md

Lines changed: 89 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,50 @@
11
# Jinja2Cpp
22

3+
[![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/)
4+
[![Standard](https://img.shields.io/badge/c%2B%2B-14-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)
35
[![Build Status](https://travis-ci.org/flexferrum/Jinja2Cpp.svg?branch=master)](https://travis-ci.org/flexferrum/Jinja2Cpp)
46
[![Build status](https://ci.appveyor.com/api/projects/status/19v2k3bl63jxl42f/branch/master?svg=true)](https://ci.appveyor.com/project/flexferrum/Jinja2Cpp)
57
[![Github Releases](https://img.shields.io/github/release/flexferrum/Jinja2Cpp.svg)](https://github.com/flexferrum/Jinja2Cpp/releases)
68
[![Github Issues](https://img.shields.io/github/issues/flexferrum/Jinja2Cpp.svg)](http://github.com/flexferrum/Jinja2Cpp/issues)
79
[![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/flexferrum/Jinja2Cpp/master/LICENSE)
810

9-
C++ implementation of big subset of Jinja 2 template engine features. This library was inspired by [Jinja2CppLight](https://github.com/hughperkins/Jinja2CppLight) project and brings support of mostly all Jinja 2 templates features into C++ world. Unlike [inja](https://github.com/pantor/inja) lib, you have to build Jinja2Cpp, but it has only one dependence: boost.
11+
# Table of contents
12+
13+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
14+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
15+
16+
17+
- [Introduction](#introduction)
18+
- [Getting started](#getting-started)
19+
- [More complex example](#more-complex-example)
20+
- [The simplest case](#the-simplest-case)
21+
- [Reflection](#reflection)
22+
- ['set' statement](#set-statement)
23+
- [Other features](#other-features)
24+
- [Current Jinja2 support](#current-jinja2-support)
25+
- [Supported compilers](#supported-compilers)
26+
- [Build and install](#build-and-install)
27+
- [Additional CMake build flags](#additional-cmake-build-flags)
28+
- [Link with you projects](#link-with-you-projects)
29+
- [Changelog](#changelog)
30+
- [Version 0.6](#version-06)
31+
32+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
33+
34+
# Introduction
35+
36+
C++ implementation of big subset of Jinja2 template engine features. This library was inspired by [Jinja2CppLight](https://github.com/hughperkins/Jinja2CppLight) project and brings support of mostly all Jinja2 templates features into C++ world. Unlike [inja](https://github.com/pantor/inja) lib, you have to build Jinja2Cpp, but it has only one dependence: boost.
1037

1138
Main features of Jinja2Cpp:
1239
- Easy-to-use public interface. Just load templates and render them.
13-
- Support for both narrow- and wide-character strings both for templates and parameters.
40+
- Conformance to [Jinja2 specification](http://jinja.pocoo.org/docs/2.10/)
41+
- Partial support for both narrow- and wide-character strings both for templates and parameters.
1442
- Built-in reflection for C++ types.
15-
- Powerful Jinja 2 expressions with filtering (via '|' operator) and 'if' expressions.
16-
- Main control statements (set, for, if).
43+
- Powerful full-featured Jinja2 expressions with filtering (via '|' operator) and 'if'-expressions.
44+
- Basic control statements (set, for, if).
45+
- Templates extention.
1746

18-
For example, this simple code:
47+
For instance, this simple code:
1948

2049
```c++
2150
std::string source = R"(
@@ -44,7 +73,7 @@ hello; world!!!
4473
In order to use Jinja2Cpp in your project you have to:
4574
* Clone the Jinja2Cpp repository
4675
* Build it according with the instructions
47-
* Link with your project.
76+
* Link to your project.
4877

4978
Usage of Jinja2Cpp in the code is pretty simple:
5079
1. Declare the jinja2::Template object:
@@ -99,97 +128,84 @@ inline const char* AnimalsToString(Animals e)
99128
return "Cat";
100129
case Monkey:
101130
return "Monkey";
102-
case Dog:
131+
case Elephant:
103132
return "Elephant";
104133
}
105134
return "Unknown Item";
106135
}
107136
```
108137
109-
Of course, you can write this producer in the way like this:
138+
Of course, you can write this producer in the way like [this](https://github.com/flexferrum/autoprogrammer/blob/87a9dc8ff61c7bdd30fede249757b71984e4b954/src/generators/enum2string_generator.cpp#L140). It's too complicated for writing 'from scratch'. Actually, there is a better and simpler way.
110139
140+
### The simplest case
141+
142+
Firstly, you should define the simple jinja2 template (in the C++ manner):
111143
```c++
112-
// Enum item to string conversion writer
113-
void Enum2StringGenerator::WriteEnumToStringConversion(CppSourceStream &hdrOs, const reflection::EnumInfoPtr &enumDescr)
144+
std::string enum2StringConvertor = R"(
145+
inline const char* {{enumName}}ToString({{enumName}} e)
114146
{
115-
auto scopedParams = MakeScopedParams(hdrOs, enumDescr);
116-
117-
out::BracedStreamScope fnScope("inline const char* $enumName$ToString($enumScopedName$ e)", "\n");
118-
hdrOs << out::new_line(1) << fnScope;
147+
switch (e)
119148
{
120-
out::BracedStreamScope switchScope("switch (e)", "\n");
121-
hdrOs << out::new_line(1) << switchScope;
122-
out::OutParams innerParams;
123-
for (auto& i : enumDescr->items)
124-
{
125-
innerParams["itemName"] = i.itemName;
126-
hdrOs << out::with_params(innerParams)
127-
<< out::new_line(-1) << "case $prefix$$itemName$:"
128-
<< out::new_line(1) << "return \"$itemName$\";";
129-
}
149+
{% for item in items %}
150+
case {{item}}:
151+
return "{{item}}";
152+
{% endfor %}
130153
}
131-
hdrOs << out::new_line(1) << "return \"Unknown Item\";";
132-
}
154+
return "Unknown Item";
155+
})";
133156
```
157+
As you can see, this template is similar to the C++ sample code above, but some parts replaced by placeholders ("parameters"). These placeholders will be replaced with the actual text during template rendering process. In order to this happen, you should fill up the rendering parameters. This is a simple dictionary which maps the parameter name to the corresponding value:
134158

135-
Too complicated for writing 'from scratch'. Actually, there is a better and simpler way.
159+
```c++
160+
jinja2::ValuesMap params {
161+
{"enumName", "Animals"},
162+
{"items", {"Dog", "Cat", "Monkey", "Elephant"}},
163+
};
164+
```
165+
An finally, you can render this template with Jinja2Cpp library:
136166

137-
### The simplest case
138-
Firstly, you have to define enum description structure:
167+
```c++
168+
jinja2::Template tpl;
169+
tpl.Load(enum2StringConvertor);
170+
std::cout << tpl.RenderAsString(params);
171+
```
172+
And you will get on the console the conversion function mentioned above!
173+
174+
You can call 'Render' method many times, with different parameters set, from several threads. Everything will be fine: every time you call 'Render' you will get the result depended only on provided params. Also you can render some part of template many times (for different parameters) with help of 'for' loop and 'extend' statement (described below). It allows you to iterate through the list of items from the first to the last one and render the loop body for each item respectively. In this particular sample it allows to put as many 'case' blocks in conversion function as many items in the 'reflected' enum.
175+
176+
### Reflection
177+
Let's imagine you don't want to fill the enum descriptor by hand, but want to fill it with help of some code parsing tool ([autoprogrammer](https://github.com/flexferrum/autoprogrammer) or [cppast](https://github.com/foonathan/cppast)). In this case you can define structure like this:
139178

140179
```c++
141180
// Enum declaration description
142181
struct EnumDescriptor
143182
{
144-
// Enumeration name
145-
std::string enumName;
146-
// Namespace scope prefix
147-
std::string nsScope;
148-
// Collection of enum items
149-
std::vector<std::string> enumItems;
183+
// Enumeration name
184+
std::string enumName;
185+
// Namespace scope prefix
186+
std::string nsScope;
187+
// Collection of enum items
188+
std::vector<std::string> enumItems;
150189
};
151190
```
152-
153-
This structure holds the enum name, enum namespace scope prefix, and list of enum items (we need just names). Then, you can create populate instances of this descriptor automatically using clang front-end (ex. here: [clang-based enum2string converter generator](https://github.com/flexferrum/flex_lib/blob/accu2017/tools/codegen/src/main.cpp) ). For our sample we create the instance manually:
154-
191+
This structure holds the enum name, enum namespace scope prefix, and list of enum items (we need just names). Then, you can populate instances of this descriptor automatically using chosen tool (ex. here: [clang-based enum2string converter generator](https://github.com/flexferrum/flex_lib/blob/accu2017/tools/codegen/src/main.cpp) ). For our sample we can create the instance manually:
155192
```c++
156193
EnumDescriptor descr;
157194
descr.enumName = "Animals";
158195
descr.nsScope = "";
159196
descr.enumItems = {"Dog", "Cat", "Monkey", "Elephant"};
160197
```
161198
162-
Secondly, you can define the jinja2 template (in the C++ manner):
163-
```c++
164-
std::string enum2StringConvertor = R"(inline const char* {{enumName}}ToString({{enumName}} e)
165-
{
166-
switch (e)
167-
{
168-
{% for item in items %}
169-
case {{item}}:
170-
return "{{item}}";
171-
{% endfor %}
172-
}
173-
return "Unknown Item";
174-
})";
175-
```
176-
And finally, you can render this template with Jinja2Cpp library:
177-
199+
And now you need to transfer data from this internal enum descriptor to Jinja2 value params map. Of course it's possible to do it by hands:
178200
```c++
179201
jinja2::ValuesMap params {
180202
{"enumName", descr.enumName},
181203
{"nsScope", descr.nsScope},
182204
{"items", {descr.enumItems[0], descr.enumItems[1], descr.enumItems[2], descr.enumItems[3]}},
183205
};
184-
185-
jinja2::Template tpl;
186-
tpl.Load(enum2StringConvertor);
187-
std::cout << tpl.RenderAsString(params);
188206
```
189-
And you will get on the console the conversion function mentioned above.
190207

191-
### Reflection
192-
Actually, with Jinja2Cpp you don't need to transfer data from your internal structures to the Jinja2 values map. Library can do it for you. You just need to define reflection rules. Something like this:
208+
But actually, with Jinja2Cpp you don't have to do it manually. Library can do it for you. You just need to define reflection rules. Something like this:
193209

194210
```c++
195211
namespace jinja2
@@ -209,9 +225,10 @@ struct TypeReflection<EnumDescriptor> : TypeReflected<EnumDescriptor>
209225
}
210226
};
211227
```
212-
And this case you need to correspondingly change template itself and it's invocation:
228+
And in this case you need to correspondingly change the template itself and it's invocation:
213229
```c++
214-
std::string enum2StringConvertor = R"(inline const char* {{enum.enumName}}ToString({{enum.enumName}} e)
230+
std::string enum2StringConvertor = R"(
231+
inline const char* {{enum.enumName}}ToString({{enum.enumName}} e)
215232
{
216233
switch (e)
217234
{
@@ -229,7 +246,7 @@ std::string enum2StringConvertor = R"(inline const char* {{enum.enumName}}ToStri
229246
};
230247
// ...
231248
```
232-
Every specified field will be reflected into Jinja2Cpp internal data structures and can be accessed from the template without additional efforts. Quite simply!
249+
Every specified field will be reflected into Jinja2Cpp internal data structures and can be accessed from the template without additional efforts. Quite simply! As you can see, you can use 'dot' notation to access named members of some parameter as well, as index notation like this: `enum['enumName']`. With index notation you can access to the particular item of a list: `enum.items[3]` or `enum.items[itemIndex]` or `enum['items'][itemIndex]`.
233250

234251
### 'set' statement
235252
But what if enum `Animals` will be in the namespace?
@@ -248,7 +265,8 @@ enum Animals
248265
```
249266
In this case you need to prefix both enum name and it's items with namespace prefix in the generated code. Like this:
250267
```c++
251-
std::string enum2StringConvertor = R"(inline const char* {{enum.enumName}}ToString({{enum.nsScope}}::{{enum.enumName}} e)
268+
std::string enum2StringConvertor = R"(
269+
inline const char* {{enum.enumName}}ToString({{enum.nsScope}}::{{enum.enumName}} e)
252270
{
253271
switch (e)
254272
{
@@ -260,7 +278,7 @@ std::string enum2StringConvertor = R"(inline const char* {{enum.enumName}}ToStri
260278
return "Unknown Item";
261279
})";
262280
```
263-
This template will produce 'world::' prefix for our new enum (and enum itmes). And '::' for the previous one. But you may want to get rid of unnecessary global scope prefix. And you can do it this way:
281+
This template will produce 'world::' prefix for our new scoped enum (and enum itmes). And '::' for the ones in global scope. But you may want to eliminate the unnecessary global scope prefix. And you can do it this way:
264282
```c++
265283
{% set prefix = enum.nsScope + '::' if enum.nsScope else '' %}
266284
std::string enum2StringConvertor = R"(inline const char* {{enum.enumName}}ToString({{prefix}}::{{enum.enumName}} e)
@@ -276,12 +294,12 @@ std::string enum2StringConvertor = R"(inline const char* {{enum.enumName}}ToStri
276294
})";
277295
```
278296
This template uses two significant jinja2 template features:
279-
1. 'set' statement. You can declare new variables in your template. And you can access them by the name.
280-
2. if-expressions. It works like a ternary '?:' operator in C/C++. In C++ the code from the sample could be written this way:
297+
1. The 'set' statement. You can declare new variables in your template. And you can access them by the name.
298+
2. if-expression. It works like a ternary '?:' operator in C/C++. In C++ the code from the sample could be written in this way:
281299
```c++
282300
std::string prefix = !descr.nsScope.empty() ? descr.nsScope + "::" : "";
283301
```
284-
I.e. left part of this expression (before 'if') is a true-branch of the statement. Right part (after 'else') - false-branch, which could be omitted. As a condition you can use any expression, convertible to bool.
302+
I.e. left part of this expression (before 'if') is a true-branch of the statement. Right part (after 'else') - false-branch, which can be omitted. As a condition you can use any expression convertible to bool.
285303

286304
## Other features
287305
The render procedure is stateless, so you can perform several renderings simultaneously in different threads. Even if you pass parameters:
@@ -442,7 +460,8 @@ target_link_libraries(YourTarget
442460
#...
443461
```
444462

445-
# New in version 0.6
463+
# Changelog
464+
## Version 0.6
446465
* A lot of filters has been implemented. Full set of supported filters listed here: https://github.com/flexferrum/Jinja2Cpp/issues/7
447466
* A lot of testers has been implemented. Full set of supported testers listed here: https://github.com/flexferrum/Jinja2Cpp/issues/8
448467
* 'Contatenate as string' operator ('~') has been implemented

0 commit comments

Comments
 (0)