Text Templates in Cilk

Cilk offers built-in support for creating dynamic content or showing customized output to the user with the <cilk/template> library. This library provides an API for generating text content dynamically.

#include <cilk/cilk.h>
#include <cilk/template.h>
#include <iostream>
#include <vector>
#include <map>

int main() {
    // We can create a new template and parse its body from
    // a string. Templates are a mix of static text and "actions" enclosed in
    // {{...}} that are used to dynamically insert content.
    cilk::template t1("t1");
    try {
        t1.parse("Value is {{.}}\n");
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    // By "executing" the template we generate its text with
    // specific values for its actions. The {{.}} action is
    // replaced by the value passed as a parameter to execute.
    t1.execute(std::cout, "some text");
    t1.execute(std::cout, 5);
    t1.execute(std::cout, std::vector<std::string>{"Cilk", "C++", "OpenMP", "TBB"});

    // Helper function we'll use below.
    auto create = [](const std::string& name, const std::string& t) {
        cilk::template temp(name);
        temp.parse(t);
        return temp;
    };

    // If the data is a struct we can use the {{.FieldName}} action to access
    // its fields. The fields should be public to be accessible when a
    // template is executing.
    auto t2 = create("t2", "Name: {{.Name}}\n");

    struct Person {
        std::string Name;
    };

    t2.execute(std::cout, Person{"Jane Doe"});

    // The same applies to maps; with maps there is no restriction on the
    // case of key names.
    t2.execute(std::cout, std::map<std::string, std::string>{{"Name", "Mickey Mouse"}});

    // if/else provide conditional execution for templates. A value is considered
    // false if it's the default value of a type, such as 0, an empty string,
    // nullptr, etc.
    auto t3 = create("t3", "{{if .}}yes{{else}}no{{end}}\n");
    t3.execute(std::cout, "not empty");
    t3.execute(std::cout, "");

    // range blocks let us loop through vectors, arrays, maps or other iterable types.
    // Inside the range block {{.}} is set to the current item of the iteration.
    auto t4 = create("t4", "Range: {{range .}}{{.}} {{end}}\n");
    t4.execute(std::cout, std::vector<std::string>{"Cilk", "C++", "OpenMP", "TBB"});

    return 0;
}

This Cilk code demonstrates the use of text templates, which are similar in concept to those in other languages. The cilk::template class is used to create and manipulate templates. The syntax for template actions (enclosed in {{...}}) is kept similar to maintain familiarity.

To run this program, you would compile it with a Cilk-aware compiler and then execute the resulting binary:

$ clang++ -fcilkplus templates.cpp -o templates
$ ./templates
Value is some text
Value is 5
Value is [Cilk C++ OpenMP TBB]
Name: Jane Doe
Name: Mickey Mouse
yes
no
Range: Cilk C++ OpenMP TBB 

This example showcases various features of Cilk’s templating system, including variable interpolation, conditional statements, and iteration over collections. It’s important to note that the exact implementation details may vary depending on the specific Cilk runtime and libraries available.