Skip to content

Commit 80b2db0

Browse files
committed
Merge branch 'master' of github.com:flexferrum/Jinja2Cpp
2 parents e8b496f + a48ab1a commit 80b2db0

File tree

6 files changed

+543
-51
lines changed

6 files changed

+543
-51
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ install(TARGETS ${LIB_TARGET_NAME}
172172
ARCHIVE DESTINATION lib/static)
173173

174174
install (DIRECTORY include/ DESTINATION include)
175+
install (DIRECTORY thirdparty/nonstd/expected-light/include/ DESTINATION include)
175176
install (FILES cmake/public/FindJinja2Cpp.cmake DESTINATION cmake)
176177

177178
add_test(NAME jinja2cpp_tests COMMAND jinja2cpp_tests)

README.md

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![Build status](https://ci.appveyor.com/api/projects/status/19v2k3bl63jxl42f/branch/master?svg=true)](https://ci.appveyor.com/project/flexferrum/Jinja2Cpp)
77
[![Coverage Status](https://codecov.io/gh/flexferrum/Jinja2Cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/flexferrum/Jinja2Cpp)
88
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d932d23e9288404ba44a1f500ab42778)](https://www.codacy.com/app/flexferrum/Jinja2Cpp?utm_source=github.com&utm_medium=referral&utm_content=flexferrum/Jinja2Cpp&utm_campaign=Badge_Grade)
9-
[![Github Releases](https://img.shields.io/github/release/flexferrum/Jinja2Cpp.svg)](https://github.com/flexferrum/Jinja2Cpp/releases)
9+
[![Github Releases](https://img.shields.io/github/release/flexferrum/Jinja2Cpp/all.svg)](https://github.com/flexferrum/Jinja2Cpp/releases)
1010
[![Github Issues](https://img.shields.io/github/issues/flexferrum/Jinja2Cpp.svg)](http://github.com/flexferrum/Jinja2Cpp/issues)
1111
[![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/flexferrum/Jinja2Cpp/master/LICENSE)
1212

@@ -25,6 +25,7 @@ C++ implementation of big subset of Jinja2 template engine features. This librar
2525
- [Reflection](#reflection)
2626
- ['set' statement](#set-statement)
2727
- ['extends' statement](#extends-statement)
28+
- [Macros](#macros)
2829
- [Error reporting](#error-reporting)
2930
- [Other features](#other-features)
3031
- [Current Jinja2 support](#current-jinja2-support)
@@ -33,6 +34,7 @@ C++ implementation of big subset of Jinja2 template engine features. This librar
3334
- [Additional CMake build flags](#additional-cmake-build-flags)
3435
- [Link with you projects](#link-with-you-projects)
3536
- [Changelog](#changelog)
37+
- [Version 0.9](#version-09)
3638
- [Version 0.6](#version-06)
3739

3840
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -45,8 +47,9 @@ Main features of Jinja2Cpp:
4547
- Partial support for both narrow- and wide-character strings both for templates and parameters.
4648
- Built-in reflection for C++ types.
4749
- Powerful full-featured Jinja2 expressions with filtering (via '|' operator) and 'if'-expressions.
48-
- Basic control statements (set, for, if).
50+
- Control statements (set, for, if).
4951
- Templates extention.
52+
- Macros
5053
- Rich error reporting.
5154

5255
For instance, this simple code:
@@ -60,6 +63,7 @@ std::string source = R"(
6063
)";
6164

6265
Template tpl;
66+
tpl.Load(source);
6367

6468
std::string result = tpl.RenderAsString(ValuesMap());
6569
```
@@ -381,6 +385,54 @@ private:
381385

382386
'**extends**' statement here defines the template to extend. Set of '**block**' statements after defines actual filling of the corresponding blocks from the extended template. If block from the extended template contains something (like ```namespaced_decls``` from the example above), this content can be rendered with help of '**super()**' function. In other case the whole content of the block will be replaced. More detailed description of template inheritance feature can be found in [Jinja2 documentation](http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance).
383387

388+
## Macros
389+
Ths sample above violates 'DRY' rule. It contains the code which could be generalized. And Jinja2 supports features for such kind generalization. This feature called '**macro**'. The sample can be rewritten the following way:
390+
```c++
391+
{% macro MethodsDecl(class, access) %}
392+
{% for method in class.methods | rejectattr('isImplicit') | selectattr('accessType', 'in', access) %}
393+
{{ method.fullPrototype }};
394+
{% endfor %}
395+
{% endmacro %}
396+
397+
class {{ class.name }}
398+
{
399+
public:
400+
{{ MethodsDecl(class, ['Public']) }}
401+
protected:
402+
{{ MethodsDecl(class, ['Protected']) }}
403+
private:
404+
{{ MethodsDecl(class, ['Private', 'Undefined']) }}
405+
};
406+
407+
{% endfor %}
408+
```
409+
410+
`MethodsDecl` statement here is a **macro** which takes two arguments. First one is a class with method definitions. The second is a tuple of access specifiers. Macro takes non-implicit methods from the methods collection (`rejectattr('isImplicit')` filter) then select such methods which have right access specifier (`selectattr('accessType', 'in', access)`), then just prints the method full prototype. Finally, the macro is invoked as a regular function call: `MethodsDecl(class, ['Public'])` and replaced with rendered macro body.
411+
412+
There is another way to invoke macro: the **call** statement. Simply put, this is a way to call macro with *callback*. Let's take another sample:
413+
414+
```c++
415+
{% macro InlineMethod(resultType='void', methodName, methodParams=[]) %}
416+
inline {{ resultType }} {{ methodName }}({{ methodParams | join(', ') }} )
417+
{
418+
{{ caller() }}
419+
}
420+
{% endmacro %}
421+
422+
{% call InlineMethod('const char*', enum.enumName + 'ToString', [enum.nsScope ~ '::' ~ enum.enumName ~ ' e']) %}
423+
switch (e)
424+
{
425+
{% for item in enum.items %}
426+
case {{enum.nsScope}}::{{item}}:
427+
return "{{item}}";
428+
{% endfor %}
429+
}
430+
return "Unknown Item";
431+
{% endcall %}
432+
```
433+
434+
Here is an `InlineMacro` which just describe the inline method definition skeleton. This macro doesn't contain the actual method body. Instead of this it calls `caller` special function. This function invokes the special **callback** macro which is a body of `call` statement. And this macro can have parameters as well. More detailed this mechanics described in the [Jinja2 documentation](http://jinja.pocoo.org/docs/2.10/templates/#macros).
435+
384436
## Error reporting
385437
It's difficult to write complex template completely without errors. Missed braces, wrong characters, incorrect names... Everything is possible. So, it's crucial to be able to get informative error report from the template engine. Jinja2Cpp provides such kind of report. ```Template::Load``` method (and TemplateEnv::LoadTemplate respectively) return instance of ```ErrorInfo``` class which contains details about the error. These details include:
386438
- Error code
@@ -463,8 +515,10 @@ Currently, Jinja2Cpp supports the limited number of Jinja2 features. By the way,
463515
- 'for' statement (with 'else' branch and 'if' part support)
464516
- 'extends' statement
465517
- 'set' statement
466-
- 'extends' statement
518+
- 'extends'/'block' statements
519+
- 'macro'/'call' statements
467520
- recursive loops
521+
- space control
468522

469523
# Supported compilers
470524
Compilation of Jinja2Cpp tested on the following compilers (with C++14 enabled feature):
@@ -476,7 +530,7 @@ Compilation of Jinja2Cpp tested on the following compilers (with C++14 enabled f
476530
- Microsoft Visual Studio 2017 x86
477531

478532
# Build and install
479-
Jinja2Cpp has got only one external dependency: boost library (at least version 1.55). Because of types from boost are used inside library, you should compile both your projects and Jinja2Cpp library with similar compiler settings. Otherwise ABI could be broken.
533+
Jinja2Cpp has got only two external dependency: boost library (at least version 1.55) and expected-lite. Because of types from boost are used inside library, you should compile both your projects and Jinja2Cpp library with similar compiler settings. Otherwise ABI could be broken.
480534

481535
In order to compile Jinja2Cpp you need:
482536

@@ -562,6 +616,14 @@ target_link_libraries(YourTarget
562616
```
563617

564618
# Changelog
619+
## Version 0.9
620+
* Support of 'extents'/'block' statements
621+
* Support of 'macro'/'call' statements
622+
* Rich error reporting
623+
* Support for recursive loops
624+
* Support for space control before and after control blocks
625+
* Improve reflection
626+
565627
## Version 0.6
566628
* A lot of filters has been implemented. Full set of supported filters listed here: https://github.com/flexferrum/Jinja2Cpp/issues/7
567629
* A lot of testers has been implemented. Full set of supported testers listed here: https://github.com/flexferrum/Jinja2Cpp/issues/8

include/jinja2cpp/template_env.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ namespace jinja2
1414
class IErrorHandler;
1515
class IFilesystemHandler;
1616

17+
struct Settings
18+
{
19+
bool useLineStatements = false;
20+
bool trimBlocks = false;
21+
bool lstripBlocks = false;
22+
};
23+
1724
class TemplateEnv
1825
{
1926
public:
@@ -25,6 +32,11 @@ class TemplateEnv
2532
{
2633
return m_errorHandler;
2734
}
35+
36+
const Settings& GetSettings() const {return m_settings;}
37+
Settings& GetSettings() {return m_settings;}
38+
void SetSettings(const Settings& setts) {m_settings = setts;}
39+
2840
void AddFilesystemHandler(std::string prefix, FilesystemHandlerPtr h)
2941
{
3042
m_filesystemHandlers.push_back(FsHandler{std::move(prefix), h});
@@ -44,6 +56,7 @@ class TemplateEnv
4456
FilesystemHandlerPtr handler;
4557
};
4658
std::vector<FsHandler> m_filesystemHandlers;
59+
Settings m_settings;
4760
};
4861

4962
} // jinja2

src/template_impl.h

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,16 @@ class TemplateImpl : public ITemplateImpl
5353
TemplateImpl(TemplateEnv* env)
5454
: m_env(env)
5555
{
56+
if (env)
57+
m_settings = env->GetSettings();
5658
}
5759

5860
auto GetRenderer() const {return m_renderer;}
5961

6062
boost::optional<ErrorInfoTpl<CharT>> Load(std::basic_string<CharT> tpl, std::string tplName)
6163
{
6264
m_template = std::move(tpl);
63-
TemplateParser<CharT> parser(&m_template, tplName.empty() ? std::string("noname.j2tpl") : std::move(tplName));
65+
TemplateParser<CharT> parser(&m_template, m_settings, tplName.empty() ? std::string("noname.j2tpl") : std::move(tplName));
6466

6567
auto parseResult = parser.Parse();
6668
if (!parseResult)
@@ -72,31 +74,31 @@ class TemplateImpl : public ITemplateImpl
7274

7375
void Render(std::basic_ostream<CharT>& os, const ValuesMap& params)
7476
{
75-
if (m_renderer)
77+
if (!m_renderer)
78+
return;
79+
80+
InternalValueMap intParams;
81+
for (auto& ip : params)
7682
{
77-
InternalValueMap intParams;
78-
for (auto& ip : params)
79-
{
80-
auto valRef = &ip.second.data();
81-
auto newParam = boost::apply_visitor(visitors::InputValueConvertor(), *valRef);
82-
if (!newParam)
83-
intParams[ip.first] = std::move(ValueRef(static_cast<const Value&>(*valRef)));
84-
else
85-
intParams[ip.first] = newParam.get();
86-
}
87-
RendererCallback callback(this);
88-
RenderContext context(intParams, &callback);
89-
InitRenderContext(context);
90-
OutStream outStream(
91-
[this, &os](const void* ptr, size_t length) {
92-
os.write(reinterpret_cast<const CharT*>(ptr), length);
93-
},
94-
[this, &os](const InternalValue& val) {
95-
Apply<visitors::ValueRenderer<CharT>>(val, os);
96-
}
97-
);
98-
m_renderer->Render(outStream, context);
83+
auto valRef = &ip.second.data();
84+
auto newParam = boost::apply_visitor(visitors::InputValueConvertor(), *valRef);
85+
if (!newParam)
86+
intParams[ip.first] = std::move(ValueRef(static_cast<const Value&>(*valRef)));
87+
else
88+
intParams[ip.first] = newParam.get();
89+
}
90+
RendererCallback callback(this);
91+
RenderContext context(intParams, &callback);
92+
InitRenderContext(context);
93+
OutStream outStream(
94+
[this, &os](const void* ptr, size_t length) {
95+
os.write(reinterpret_cast<const CharT*>(ptr), length);
96+
},
97+
[this, &os](const InternalValue& val) {
98+
Apply<visitors::ValueRenderer<CharT>>(val, os);
9999
}
100+
);
101+
m_renderer->Render(outStream, context);
100102
}
101103

102104
InternalValueMap& InitRenderContext(RenderContext& context)
@@ -150,6 +152,7 @@ class TemplateImpl : public ITemplateImpl
150152

151153
private:
152154
TemplateEnv* m_env;
155+
Settings m_settings;
153156
std::basic_string<CharT> m_template;
154157
RendererPtr m_renderer;
155158
};

0 commit comments

Comments
 (0)