• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

examples/H03-May-2022-1,061738

include/kiste/H03-May-2022-589331

src/H03-May-2022-1,6841,256

syntax-files/H16-Mar-2017-5944

tests/H03-May-2022-759579

.appveyor.ymlH A D16-Mar-2017405 2619

.clang-formatH A D16-Mar-20171.7 KiB5453

.travis.ymlH A D16-Mar-20171.3 KiB4939

LICENSEH A D16-Mar-20171.3 KiB2519

README.mdH A D16-Mar-20177.9 KiB262211

pre-commitH A D16-Mar-20175.6 KiB154117

README.md

1# kiss-templates
2Type safe _"Keep it simple, stupid"_ text templates for C++. If you are familiar with the idea of text templates and with C++, you can learn how to use it in just a few minutes.
3
4Branch / Compiler | clang-3.4,  gcc-4.8   |  VS14 2015
5------------------| ----------------------|-------------
6Master       | [![Build Status](https://travis-ci.org/rbock/kiss-templates.svg?branch=master)](https://travis-ci.org/rbock/kiss-templates) | [![Build status](https://ci.appveyor.com/api/projects/status/0vhmquucorgyx427/branch/master?svg=true)](https://ci.appveyor.com/project/rbock/kiss-templates/branch/master)
7Develop      | [![Build Status](https://travis-ci.org/rbock/kiss-templates.svg?branch=develop)](https://travis-ci.org/rbock/kiss-templates) | [![Build status](https://ci.appveyor.com/api/projects/status/0vhmquucorgyx427/branch/develop?svg=true)](https://ci.appveyor.com/project/rbock/kiss-templates/branch/develop)
8
9## How it works
10Use kiste2cpp to turn text templates into type- and name-safe C++ code. Use this code to generate text from your data and the serializer of choice.
11
12### What templates look like
13Template are a mix of
14  - kiss template commands (there are VERY few)
15  - C++
16  - text
17
18```
19%namespace test
20%{
21  $class Hello
22
23  %auto render() -> void
24  %{
25    Hello ${data.name}!
26  %}
27
28  $endclass
29%}
30```
31
32### Generating C++ code
33I bet you can guess what this all means (and it is documented below), so let's compile this into a C++ header file:
34
35```sh
36kiste2cpp hello_world.kiste > hello_world.h
37```
38
39### Using the generated code
40And now we use it in our C++ project like this
41
42```C++
43#include <iostream>
44#include <hello_world.h>
45#include <kiste/raw.h>
46
47struct Data
48{
49  std::string name;
50};
51
52int main()
53{
54  const auto data = Data{"World"};
55  auto& os = std::cout;
56  auto serializer = kiste::raw{os};
57  auto hello = test::Hello(data, serializer);
58
59  hello.render();
60}
61```
62
63### Output
64Compile and run:
65```sh
66$ ./examples/0_hello_world/hello_world
67    Hello World!
68```
69Yeah!
70
71## Short Reference
72  - `%<whatever>` C++ code
73  - `$class <name>` starts a template class
74  - `$class <name> : <base>` starts a template class, which inherits from a base class
75  - `$member <class> <name>` adds a template as a member
76  - `$endclass` ends a template class
77  - `${<expression>}` send expression to serializer (which takes care of encoding, quoting, escaping, etc)
78  - `$raw{<expression>}` send expression to the ostream directly (no escaping)
79  - `$call{<function>}` call a function (do not serialize result)
80  - `$|` trim left/right
81  - `$$` and `$%` escape `$` and `%` respectively
82  - Anything else inside a function of a template class is text
83
84## The kiss template syntax
85
86### C++
87Any line that starts with zero or more spaces and a `%` is interpreted as C++.
88For example
89```
90%#include<string>
91%namespace
92%{
93    %auto foo() -> std::string
94    %{
95      return "bar";
96    %}
97%}
98```
99There is really nothing to it, just a `%` at the beginning of the line
100
101### Template classes
102All text of the template is located in functions of template classes. Template classes start with `$class <name> [: <base>]` and end with `$endclass`.
103
104This is a stand-alone class:
105```
106$class base
107% // Some stuff
108$endclass
109```
110And this is a derived class
111```
112%#include <base.h>
113$class derived : base
114% // Some other stuff
115$endclass
116```
117In the generated code, the parent will also be the base of the child. They are linked in such a way that
118
119  - you can access the direct child via a `child` member variable in the parent
120  - you can access anything inherited from parent, grandparent, etc via a `parent` member
121
122### Member templates
123If you want to reuse some template elements or just want to organize your templates into smaller units and use composition.
124
125A helper class
126```
127$class Helper
128% // Some stuff
129$endclass
130```
131
132And this is a composite class
133```
134%#include <Helper.h>
135$class composite
136$member Helper helper
137% // Some other stuff
138$endclass
139```
140
141In the generated code, the member template will also be a member of the composite. They are linked in such a way that
142
143  - you can access the member via its name in the composite
144  - you can access the composite as `child` from the member template
145
146
147### Serializing data
148As you saw in the initial example, the generated template code is initialized with data and a serializer. You can serialize members of that data or in fact any C++ expression by enclosing it in `${}`. For instance
149
150```
151%for (const auto& entry : data.entries)
152%{
153  First name: ${entry.first}
154  Last name: ${entry.last}
155  Size of names: ${entry.first.size() + entry.last.size()}
156%}
157```
158
159The serializer takes care of the required escaping, quoting, encoding, etc.
160
161### Raw data
162Sometimes you need to actually output some text as is. Then use `$raw{expression}`. It will just pipe whatever you give it to the `ostream` directly.
163
164### Calling functions
165If you want to call a function without serializing the result (e.g. because the function returns `void`), you can enclose the call in `$call{}`.
166
167### Trimming
168  - left-trim of a line: Zero or more spaces/tabs followed by `$|`
169  - right-trim of a line (including the trailing return): `$|` at the end of the line
170
171For example:
172```
173%auto title() -> void
174%{
175   $| Hello ${data.name}! $|
176%}
177```
178This will get rid of the leading spaces and the trailing return, yielding something like
179
180```
181 Hello Mr. Wolf!
182```
183
184### Escape sequences
185  - `$$` -> `$`
186  - `$%` -> `%`
187
188### Text
189Text is everything else, as long as it is inside a function of a template class.
190
191## Serializer classes
192The interface of a serializer has to have
193
194  - `auto text(const char*) -> void;` This function is called by the kiss templates to serialize their texts.
195  - `auto escape(...) -> void;` This function is called with expressions from `${whatever}`. Make it accept whatever you need and like.
196
197Optionally, the serializer might offer
198
199  - `auto raw(...) -> void;` This function is called with expressions from `$raw{whatever}`. Make it accept whatever you need and like.
200  - `auto report_exception(long lineNo, const std::string& expression, std::exception_ptr e);` This function gets called if kiste2cpp is called with --report-exceptions. Handle reported exceptions here in any way you seem fit.
201
202## Serializer policies
203At some point you will probably want to serialize your types.
204If extending of `kiste::html` for one or two types works,
205extending for more types is not flexible, especially if you want to customize your serializer.
206
207Then this is a moment when _serializer policies_ may help.
208It allows you to implement a serializer for your output format in one class and
209implement policies (how to serialize specific types) as separate classes.
210Policies should know nothing about serializers, only how to convert their types to string (or even other types).
211
212Have a look at example `ratio_policy` for our type `ratio`:
213```C++
214struct ratio
215{
216  int num;
217  int den;
218};
219
220struct ratio_policy
221{
222  template <typename SerializerT>
223  void escape(SerializerT& serializer, const ratio& value)
224  {
225    serializer.escape(value.num);
226    if (value.den != 1)
227    {
228      serializer.escape('/');
229      serializer.escape(value.den);
230    }
231  }
232};
233```
234
235Then we need to extend `kiste::html` (or your serializer) with one template method `escape(SerializerT&, const T& t)`:
236```C++
237struct html : kiste::html
238{
239  html(std::ostream& os) : kiste::html(os)
240  {
241  }
242
243  template <typename SerializerT, typename T>
244  void escape(SerializerT&, const T& t)
245  {
246    kiste::html::escape(t);
247  }
248};
249```
250
251Finally we can build a serializer as `kiste::build_serializer(kiste::html{os}, ratio_policy{})`.
252`kiste::build_serializer` accepts an arbitary number of policies and builds one serializer that uses them all.
253
254This approach allows to keep knowledge about types in policies,
255provide arguments to policies and even reuse them for different serializers.
256Check out examples for more complex usages.
257
258## Further education
259This is pretty much it.
260
261There are several examples in the `examples` folder. If you have questions, please do not hesitate to open an issue.
262