{"id":18312443,"url":"https://github.com/rbock/kiss-templates","last_synced_at":"2025-04-05T18:32:05.087Z","repository":{"id":36429926,"uuid":"40734928","full_name":"rbock/kiss-templates","owner":"rbock","description":"Type safe \"Keep it simple, stupid\" text templates for C++","archived":false,"fork":false,"pushed_at":"2017-03-16T05:57:26.000Z","size":176,"stargazers_count":39,"open_issues_count":1,"forks_count":9,"subscribers_count":7,"default_branch":"develop","last_synced_at":"2025-03-21T08:51:16.464Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rbock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-14T20:34:23.000Z","updated_at":"2023-05-19T08:36:48.000Z","dependencies_parsed_at":"2022-09-02T20:00:13.376Z","dependency_job_id":null,"html_url":"https://github.com/rbock/kiss-templates","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbock%2Fkiss-templates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbock%2Fkiss-templates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbock%2Fkiss-templates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbock%2Fkiss-templates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rbock","download_url":"https://codeload.github.com/rbock/kiss-templates/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247384009,"owners_count":20930392,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-05T16:24:08.252Z","updated_at":"2025-04-05T18:32:02.326Z","avatar_url":"https://github.com/rbock.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kiss-templates\nType 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.\n\nBranch / Compiler | clang-3.4,  gcc-4.8   |  VS14 2015\n------------------| ----------------------|-------------\nMaster       | [![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)\nDevelop      | [![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)\n\n## How it works\nUse 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.\n\n### What templates look like\nTemplate are a mix of\n  - kiss template commands (there are VERY few)\n  - C++\n  - text\n\n```\n%namespace test\n%{\n  $class Hello\n\n  %auto render() -\u003e void\n  %{\n    Hello ${data.name}!\n  %}\n\n  $endclass\n%}\n```\n\n### Generating C++ code\nI bet you can guess what this all means (and it is documented below), so let's compile this into a C++ header file:\n\n```sh\nkiste2cpp hello_world.kiste \u003e hello_world.h\n```\n\n### Using the generated code\nAnd now we use it in our C++ project like this\n\n```C++\n#include \u003ciostream\u003e\n#include \u003chello_world.h\u003e\n#include \u003ckiste/raw.h\u003e\n\nstruct Data\n{\n  std::string name;\n};\n\nint main()\n{\n  const auto data = Data{\"World\"};\n  auto\u0026 os = std::cout;\n  auto serializer = kiste::raw{os};\n  auto hello = test::Hello(data, serializer);\n\n  hello.render();\n}\n```\n\n### Output\nCompile and run:\n```sh\n$ ./examples/0_hello_world/hello_world\n    Hello World!\n```\nYeah!\n\n## Short Reference\n  - `%\u003cwhatever\u003e` C++ code\n  - `$class \u003cname\u003e` starts a template class\n  - `$class \u003cname\u003e : \u003cbase\u003e` starts a template class, which inherits from a base class\n  - `$member \u003cclass\u003e \u003cname\u003e` adds a template as a member\n  - `$endclass` ends a template class\n  - `${\u003cexpression\u003e}` send expression to serializer (which takes care of encoding, quoting, escaping, etc)\n  - `$raw{\u003cexpression\u003e}` send expression to the ostream directly (no escaping)\n  - `$call{\u003cfunction\u003e}` call a function (do not serialize result)\n  - `$|` trim left/right\n  - `$$` and `$%` escape `$` and `%` respectively\n  - Anything else inside a function of a template class is text\n\n## The kiss template syntax\n\n### C++\nAny line that starts with zero or more spaces and a `%` is interpreted as C++.\nFor example\n```\n%#include\u003cstring\u003e\n%namespace\n%{\n    %auto foo() -\u003e std::string\n    %{\n      return \"bar\";\n    %}\n%}\n```\nThere is really nothing to it, just a `%` at the beginning of the line\n\n### Template classes\nAll text of the template is located in functions of template classes. Template classes start with `$class \u003cname\u003e [: \u003cbase\u003e]` and end with `$endclass`.\n\nThis is a stand-alone class:\n```\n$class base\n% // Some stuff\n$endclass\n```\nAnd this is a derived class\n```\n%#include \u003cbase.h\u003e\n$class derived : base\n% // Some other stuff\n$endclass\n```\nIn the generated code, the parent will also be the base of the child. They are linked in such a way that\n\n  - you can access the direct child via a `child` member variable in the parent\n  - you can access anything inherited from parent, grandparent, etc via a `parent` member\n\n### Member templates\nIf you want to reuse some template elements or just want to organize your templates into smaller units and use composition.\n\nA helper class\n```\n$class Helper\n% // Some stuff\n$endclass\n```\n\nAnd this is a composite class\n```\n%#include \u003cHelper.h\u003e\n$class composite\n$member Helper helper\n% // Some other stuff\n$endclass\n```\n\nIn the generated code, the member template will also be a member of the composite. They are linked in such a way that\n\n  - you can access the member via its name in the composite\n  - you can access the composite as `child` from the member template\n\n\n### Serializing data\nAs 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\n\n```\n%for (const auto\u0026 entry : data.entries)\n%{\n  First name: ${entry.first}\n  Last name: ${entry.last}\n  Size of names: ${entry.first.size() + entry.last.size()}\n%}\n```\n\nThe serializer takes care of the required escaping, quoting, encoding, etc.\n\n### Raw data\nSometimes 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.\n\n### Calling functions\nIf you want to call a function without serializing the result (e.g. because the function returns `void`), you can enclose the call in `$call{}`.\n\n### Trimming\n  - left-trim of a line: Zero or more spaces/tabs followed by `$|`\n  - right-trim of a line (including the trailing return): `$|` at the end of the line\n\nFor example:\n```\n%auto title() -\u003e void\n%{\n   $| Hello ${data.name}! $|\n%}\n```\nThis will get rid of the leading spaces and the trailing return, yielding something like\n\n```\n Hello Mr. Wolf!\n```\n\n### Escape sequences\n  - `$$` -\u003e `$`\n  - `$%` -\u003e `%`\n\n### Text\nText is everything else, as long as it is inside a function of a template class.\n\n## Serializer classes\nThe interface of a serializer has to have\n\n  - `auto text(const char*) -\u003e void;` This function is called by the kiss templates to serialize their texts.\n  - `auto escape(...) -\u003e void;` This function is called with expressions from `${whatever}`. Make it accept whatever you need and like.\n\nOptionally, the serializer might offer\n\n  - `auto raw(...) -\u003e void;` This function is called with expressions from `$raw{whatever}`. Make it accept whatever you need and like.\n  - `auto report_exception(long lineNo, const std::string\u0026 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.\n\n## Serializer policies\nAt some point you will probably want to serialize your types.\nIf extending of `kiste::html` for one or two types works,\nextending for more types is not flexible, especially if you want to customize your serializer.\n\nThen this is a moment when _serializer policies_ may help.\nIt allows you to implement a serializer for your output format in one class and\nimplement policies (how to serialize specific types) as separate classes.\nPolicies should know nothing about serializers, only how to convert their types to string (or even other types).\n\nHave a look at example `ratio_policy` for our type `ratio`:\n```C++\nstruct ratio\n{\n  int num;\n  int den;\n};\n\nstruct ratio_policy\n{\n  template \u003ctypename SerializerT\u003e\n  void escape(SerializerT\u0026 serializer, const ratio\u0026 value)\n  {\n    serializer.escape(value.num);\n    if (value.den != 1)\n    {\n      serializer.escape('/');\n      serializer.escape(value.den);\n    }\n  }\n};\n```\n\nThen we need to extend `kiste::html` (or your serializer) with one template method `escape(SerializerT\u0026, const T\u0026 t)`:\n```C++\nstruct html : kiste::html\n{\n  html(std::ostream\u0026 os) : kiste::html(os)\n  {\n  }\n\n  template \u003ctypename SerializerT, typename T\u003e\n  void escape(SerializerT\u0026, const T\u0026 t)\n  {\n    kiste::html::escape(t);\n  }\n};\n```\n\nFinally we can build a serializer as `kiste::build_serializer(kiste::html{os}, ratio_policy{})`.\n`kiste::build_serializer` accepts an arbitary number of policies and builds one serializer that uses them all.\n\nThis approach allows to keep knowledge about types in policies,\nprovide arguments to policies and even reuse them for different serializers.\nCheck out examples for more complex usages.\n\n## Further education\nThis is pretty much it.\n\nThere are several examples in the `examples` folder. If you have questions, please do not hesitate to open an issue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbock%2Fkiss-templates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frbock%2Fkiss-templates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbock%2Fkiss-templates/lists"}