Template Examples

Simple Example

One reason this example is so simple is that it doesn't even require a separate template file, but instead uses StringToTemplateCache(). It also doesn't use sections or template-includes.


int main() {
  static const char template_text[] =
    "ERROR: {{FUNCTION}}({{ARGS}}) returned {{ERROR_CODE}}: {{ERROR_MESSAGE}}\n";
  ctemplate::StringToTemplateCache("mytpl", template_text, ctemplate::DO_NOT_STRIP);
  FILE* fp = fopen(argv[1], "r");
  if (fp == NULL) {
    int err_no = errno;   // squirrel this away
    ctemplate::TemplateDictionary dict("error_msg: fopen()");
    dict.SetValue("FUNCTION", "fopen");
    dict.SetValue("ARGS", argv[1]);
    dict.SetIntValue("ERROR_CODE", err_no);
    dict.SetValue("ERROR_MESSAGE", strerror(err_no));

    string error_text;
    ctemplate::ExpandTemplate("mytpl", ctemplate::DO_NOT_STRIP, &dict, &error_text);
    puts(error_text.c_str());
  }
}

Note: If this template was intended to run in a web application, you can leverage the functionality provided by the auto-escape mode. Simply add the AUTOESCAPE pragma directive at the top of the template text and your variables will be automatically escaped for the context you specify. For example, if your template is returned in an HTML context, change the template_text declaration as follows:
  static const char template_text[] =
    "{{%AUTOESCAPE context=\"HTML\"}}"
    "ERROR: {{FUNCTION}}({{ARGS}}) returned {{ERROR_CODE}}: {{ERROR_MESSAGE}}\n";

This example is only slightly more complicated: we only print the ": <error message>" part when the error message isn't the empty string.


int main() {
  static const char template_text[] =
     "ERROR: {{FUNCTION}}({{ARGS}}) returned {{ERROR_CODE}}"
     "{{#MSG_SECTION}}: {{ERROR_MESSAGE}}{{/MSG_SECTION}}\n";
  ctemplate::StringToTemplateCache("mytpl", template_text, ctemplate::DO_NOT_STRIP);
  FILE* fp = fopen(argv[1], "r");
  if (fp == NULL) {
    int err_no = errno;   // squirrel this away
    ctemplate::TemplateDictionary dict("file_error_message");
    dict.SetValue("FUNCTION", "fopen");
    dict.SetValue("ARGS", argv[1]);
    dict.SetIntValue("ERROR_CODE", err_no);
    if (err_no > 0)
      dict.SetValueAndShowSection("ERROR_MESSAGE", strerror(err_no),
                                  "MSG_SECTION");

    string error_text;
    ctemplate::ExpandTemplate("mytpl", ctemplate::DO_NOT_STRIP, &dict, &error_text);
    puts(error_text.c_str());
  }
  delete tpl;
}

This maybe-show-text functionality is one way the template machinery is more powerful than just using printf. Another nice property of templates is you can reuse the same variable multiple times in your template string. You can also define the variable values in any order.

Search Results Page

Here is an example template that could be used to format a Google search results page:


{{>HEADER}}
<body bgcolor=white>

{{>PAGE_HEADING}}{{!The following div must be on the same line}}<div>

{{!The ONE_RESULT section displays a single search item}}
{{#ONE_RESULT}}
    {{! Note: there are two SUBITEM_SECTIONs. They both show or hide together}}
    {{#SUBITEM_SECTION}}<blockquote>{{/SUBITEM_SECTION}}
    {{! LEAD_LINE is received HTML-escaped from the backend.}}
    <p><a href="{{JUMP_TO_URL:html_escape}}"  target=nw>{{LEAD_LINE}}</a><font size=-1>

    {{! SNIPPET1, SNIPPET2 are HTML-escaped in the snippet generator.}}
    {{#SNIPPET1_SECTION}}
        <br>{{SNIPPET1}}
    {{/SNIPPET1_SECTION}}

    {{#SNIPPET2_SECTION}}
        <br>{{SNIPPET2}}
    {{/SNIPPET2_SECTION}}

    {{#DESCRIPTION_SECTION}}
        {{! DESC is received HTML-escaped from the backend.}}
        <br><span class=f>Description:</span> {{DESC}}
    {{/DESCRIPTION_SECTION}}

    {{#CATEGORY_SECTION}}
        <br><span class=f>Category:</span> <a href="{{CAT_URL:html_escape}}" class=f>
        {{CATEGORY:html_escape}}</a>
    {{/CATEGORY_SECTION}}

    {{#LASTLINE_SECTION}}
        <br><font color="{{ALT_TEXT_COLOR:h}}">{{URL:h}}
        {{#KS_SECTION}}} - {{KSIZE:h}}{{/KS_SECTION}}}
        {{#CACHE_SECTION}}} - <a href="{{CACHE_URL:h}}" class=f>Cached</A>
        {{/CACHE_SECTION}}}
        {{#SIM_SECTION}}} - <a href="{{SIM_PAGES_URL:h}}" class=f>Similar pages</A>
        {{/SIM_SECTION}}}

        {{#STOCK_SECTION}}
             -  <a href="{{STOCK_URL:h}}" class=f>Stock quotes: {{STOCK_SYMBOL:h}}</a>
        {{/STOCK_SECTION}}
        </font>
    {{/LASTLINE_SECTION}}           

    {{#MORE_SECTION}}
        <br>[ <a href="{{MORE_URL:h}}" class=f>More results from {{MORE_LABEL:h}}</a> ]
    {{/MORE_SECTION}}

    </font><br>
    {{! Note: there are two SUBITEM_SECTIONs. They both show or hide together}}
    {{#SUBITEM_SECTION}}</blockquote>{{/SUBITEM_SECTION}}
{{/ONE_RESULT}}
</div> {{! this /div closes the div at the top of this file}}
{{>PAGE_FOOTING}}

Here is a sample procedure that could populate a dictionary for expanding that template. The "one procedure" entry point is fill_search_results_dictionary. The SetTemplateValues function is a separate entry point for initializing each top-level template with some standard values.

#include "template.h"

RegisterTemplateFilename(SEARCH_RESULTS_FN, "search_results.tpl");
#include "search_results.tpl.varnames.h"  // defines ksr_HEADER, etc.

using ctemplate::TemplateDictionary;
using ctemplate::ExpandTemplate;
using ctemplate::STRIP_WHITESPACE;

// IsEmpty
//    A simple utility function
static bool IsEmpty(const string &str) {
  return str.empty();
}

// SetTemplateValues
//   Use the TemplateDictionary object to set template-wide values that
//   may be used in the top-level template and all its sub-sections
//   and included templates. The template-wide values are all
//   colors from the Palette object
void SetTemplateValues(TemplateDictionary *dictionary, const Palette* colors) {
  // better would be to use ksr_LINK_COLOR, etc, assuming those are
  // defined in search_results.tpl.varnames.h.  But using literal
  // text, as here, is legal as well.
  dictionary->SetValue("LINK_COLOR", colors->link_color);
  dictionary->SetValue("BAR_TEXT_COLOR", colors->bar_text_color);
  dictionary->SetValue("TEXT_COLOR", colors->text_color);
  dictionary->SetValue("FAINT_COLOR", colors->faint_color);
  dictionary->SetValue("IMPORTANT_COLOR", colors->important_color);
  dictionary->SetValue("BAR_COLOR", colors->bar_color);
  dictionary->SetValue("ALT_TEXT_COLOR", colors->alt_text_color);
  dictionary->SetValue("ALINK_COLOR", colors->alink_color);
  dictionary->SetValue("VLINK_COLOR", colors->vlink_color);
}

// fill_search_results_dictionary
//   Iterates through all the QueryResults contained in the Query object.
//   For each one, it sets corresponding template dictionary values
//   (or hides sections containing their variables, if appropriate) in
//   a sub-dictionary and then adds that dictionary to the parent
void fill_search_results_dictionary(TemplateDictionary *dictionary,
                                    const Query *query) {
  dictionary->SetFilename(SEARCH_RESULTS_FN);

  // These two functions are defined elsewhere
  fill_header_dictionary(dictionary->AddIncludeDictionary(ksr_HEADER));
  fill_page_heading_dictionary(dictionary->AddIncludeDictionary(ksr_PAGE_HEADING),
                               query);

  ResultsList *results = query->GetResults();
  int resCount = 0;

  for (ResultsList::const_iterator iter = results->begin();
       iter != results->end();
       ++iter) {
    QueryResult *qr = (*iter);

    // Create a new sub-dictionary named "Result Dict <n>" for this entry

    ++resCount;

    TemplateDictionary *result_dictionary =
      dictionary->AddSectionDictionary(ksr_ONE_RESULT);

    result_dictionary->SetValue(ksr_JUMP_TO_URL, qr->GetUrl());

    if (qr->IsSubItem()) {
      result_dictionary->ShowSection(ksr_SUBITEM_SECTION);
    }

    result_dictionary->SetValue(ksr_LEAD_LINE, qr->GetLeadLine());

    result_dictionary->SetValueAndShowSection(ksr_SNIPPET1, qr->GetSnippet1(),
                                              ksr_SNIPPET1_SECTION);
    
    result_dictionary->SetValueAndShowSection(ksr_SNIPPET2, qr->GetSnippet2(),
                                              ksr_SNIPPET2_SECTION);
    
    result_dictionary->SetValueAndShowSection(ksr_DESC, qr->GetDescription(),
                                              ksr_DESCRIPTION_SECTION);

    result_dictionary->SetValueAndShowSection(ksr_CAT_URL, qr->GetCategoryUrl(),
                                              ksr_CATEGORY_SECTION);

    result_dictionary->SetValueAndShowSection("CATEGORY", qr->GetCategoryName(),
                                              "CATEGORY_SECTION");


    if (IsEmpty(qr->GetDisplayUrl()) &&
        IsEmpty(qr->GetPageSize()) &&
        IsEmpty(qr->GetCachedUrl()) &&
        IsEmpty(qr->GetSimilarPagesUrl()) &&
        (IsEmpty(qr->GetStockUrl()) ||
         IsEmpty(qr->GetStockSymbol())) ) {
      // there is nothing on the last line, so hide it altogether
    } else {
      result_dictionary->ShowSection("LASTLINE_SECTION");

      result_dictionary->SetValue(ksr_URL, qr->GetDisplayUrl());

      result_dictionary->SetValueAndShowSection(ksr_KSIZE, qr->GetPageSize(),
                                                ksr_KS_SECTION);

      result_dictionary->SetValueAndShowSection(ksr_CACHE_URL, qr->GetCachedUrl(),
                                                ksr_CACHE_SECTION);

      result_dictionary->SetValueAndShowSection(ksr_SIM_PAGES_URL,
                                                qr->GetSimilarPagesUrl(),
                                                ksr_SIM_SECTION);

      result_dictionary->SetValueAndShowSection(ksr_STOCK_URL, qr->GetStockUrl(),
                                                ksr_STOCK_SECTION);

      result_dictionary->SetValueAndShowSection(ksr_STOCK_SYMBOL,
                                                qr->GetStockSymbol(),
                                                ksr_STOCK_SECTION);
    }

    result_dictionary->SetValueAndShowSection(ksr_MORE_URL, qr->GetMoreUrl(),
                                              ksr_MORE_SECTION);

    result_dictionary->SetValueAndShowSection(ksr_MORE_LABEL, qr->GetMoreLabel(),
                                              ksr_MORE_SECTION);

  }    

  fill_page_footing_dictionary(dictionary->AddIncludeDictionary(ksr_PAGE_FOOTING),
                               query);
}        

void output_page(const Query* query) {
  TemplateDictionary dict("search-results dict");
  string output;
  fill_search_results_dictionary(&dict, query);
  ctemplate::ExpandTemplate(SEARCH_RESULTS_FN, STRIP_WHITESPACE, &dict, &output);
  // output now holds the expanded template
}



Craig Silverstein