Publi

Generar una página web completa a partir de varias plantillas en C++ con Silicon

photo-1440952306150-7f239990787e

En pleno auge de PHP7, con las velocidades que conseguimos para generar contenidos para web, sigo empeñado en la generación de contenido web en C++. Aunque se complica un poco la programación, el consumo de memoria y CPU suele ser más pequeño, en teoría, ya que lo más seguro es que algunas partes de PHP las hayan creado con más idea que yo las mías en C y hayan conseguido algoritmos más eficientes. Pese a todo, me gusta tener otras alternativas, extensibles a muchos otros campos de la programación como puede ser crear un backend para un demonio de servidor/escritorio/mediacenter… para su administración por otros ordenadores dentro de la misma red.

En este caso, lo que pretendo es generar una web completa (sólo en HTML) a partir de plantillas. Además, como casi todas las páginas web dispondremos de varias partes: por un lado tendremos un layout principal donde se escriben cabeceras y código HTML común para todas las páginas de un sitio (o de una categoría), luego una plantilla de la sección actual donde estamos es decir, la vista actual y por último bloques que harán su render independiente y se empotrarán donde corresponde.

Para ello, voy a seguir utilizando Silicon (y de paso creo una especie de manual online). El objetivo de Silicon es que sea una biblioteca pequeña y no demasiado lenta, que nos permita, además de establecer reemplazos en palabras clave, llamar a funciones, ejecutar pequeñas operaciones lógicas y llamar a funciones para generar salidas más complejas entre otras cosas.

Para ello, vamos a ver un pequeño ejemplo que viene con el proyecto, en él generaremos una página con los siguientes archivos:

  • sample1.html : contiene el código de la vista actual. Estará en el directorio views/
  • sample_layout.html : contiene la estructura base de la web, etiquetas html, head, body, así como una cabecera, capa de contenido y capa para el pie. Estará dentro del directorio views/
  • sample_header.html : contiene la cabecera. También lo colocaremos en views/
  • sample.cc : archivo de C++ que generará el HTML resultante

Dentro de las plantillas encontraremos varias palabras clave:

  • ProjectTitle : título del proyecto
  • Author : autor
  • AuthorEmail : correo del autor
  • PageTitle : título de la página
  • Section0, Section1, Section2, Section3, Section4, Section5 : indicará si se mostrarán o no dichas secciones

También encontramos funciones como:

  • date : presenta la fecha actual en un formato determinado de fecha
  • includeCss : incluye un archivo CSS. Generamos el código necesario para incluir el CSS <link href=”archivo” rel=”stylesheet” type=”text/css”> Esta función la definiremos en nuestro programa principal
  • block : empotrará un bloque en ese punto de la plantilla
  • inc : incrementa una variable

Aquí tenemos los archivos:
sample1.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
{%if Section0}}
<h2>What we can do?</h2>
<ul>
    <li>Use templates as output for programs in C++11</li>
    <li>Templates may use keywords, collections and functions</li>
    <li>Use layouts as main template containing another templates.
    Just like a web page has a base design and a particular view
    for each page inside.</li>
    <li>Global keyword/function definitions to use it in all instances.</li>
    <li>No library requirements</li>
    <li>You can use it wherever you want. MIT licensed.</li>
</ul>
{!inc seccion/}
{/if}}
{%if Section1}}
<h2>Keywords</h2>
<p>Keywords are tags like <strong>{{</strong>word<strong>}}</strong> where
    <i>word</i> is a special token we choose and which will be replaced with
    a variable, or text we specify in our C++ code.</p>
<p>Silicon has also some pre-defined keywords as:</p>
<ul>
    <li>SiliconVersion : This library version. (e.g: {{SiliconVersion}})</li>
    <li>DS : Current directory separator. (e.g: {{DS}})</li>
</ul>
<p>From C++ you can define keywords by calling:</p>
<ul>
    <li>setKeyword(string keyword, string value) : for local keywords (only this instance)</li>
    <li>Silicon::setGlobalKeyword(string keyword, string value) : for global keywords</li>
</ul>
{!inc seccion/}
{/if}}
{%if Section2}}
<h2>Functions</h2>
<p>Functions are tags like <strong>\{!</strong>word<strong>/}</strong> where
    <i>word</i> is a special token we choose and which will be replaced with
    a variable, or text we specify in our C++ code.</p>
<p>Functions <strong>must</strong> be closed, so they can contain nested data
    with text, keywords, functions, an so on. We have normal and auto-closed
    function tags:</p>
<ul>
    <li>Normal: \{!functionName}}Data...text, and whatever\{/functionName}}</li>
    <li>Auto-closed: \{!functionName/} (auto-closed have no data at all.</li>
</ul>
<p>Functions may also have arguments as pairs of keys=values to modify their
    behaviour, like this:</p>
<p>\{!functionName argument1=value1 argument2=value2.../}</p>
or even like this:
<p>\{!functionName argument1=value1 argument2=value2...}}
Data, tags, text, functions, and so...
    \{/functionName}}</p>
<p>Functions can be used when declaring keywords that now always will be shown
    and require some time to compute.</p>
<p>Silicon also have some pre-defined functions{%if Section6}}(for detailed information, see below{/if}}:</p>
<ul>
    <li>SiliconTotalKeywords : Gets total keywords at this moment. (e.g: {!SiliconTotalKeywords/})</li>
    <li>date : Gets the date. (e.g: {!date/})</li>
    <li>pwd : Gets current directory. (e.g: {!pwd/})</li>
    <li>block : inserts a new block from file</li>
    <li>set : sets a value for one or more keywords</li>
    <li>inc : increment the value of one or more keywords</li>
</ul>
<p>From C++ you can define functions by calling:</p>
<ul>
    <li>setFunction(string functionName, TemplateFunction function) : for local functions</li>
    <li>Silicon::setGlobalFunction(string functionName, TemplateFunction function) : for global functions</li>
</ul>
<p>These functions must have this prototype:</p>
<p>   string myfunction(Silicon* s, Silicon::StringMap options)</p>
<p>where StringMap is just a map&lt;string, string>, containing keys and values for
    all function arguments: key1=value1, key2=value2, and so.</p>
<p>Your function must return the string being concatenated to the template.</p>
{!inc seccion/}
{/if}}
{%if Section3}}
<h2>Collections</h2>
<p>Collections are special keywords storing a number of key-value pairs. In C++, they are represented by
vector of maps, so we can iterate over the vector (rows) and get column-value pairs inside. </p>
<p>In this version, collections are accessed only by calling \{%collection}} </p>
<p>In C++, collections can be included using:</p>
<ul>
    <li>addCollection(string name, vector&lt;map&lt;string, string>> v) : to input a whole vector</li>
    <li>addToCollection(string name, StringMap m) : To append a map to the collection</li>
    <li>long addToCollection(string name, long pos, string key, string value) : To insert one key, and one value to the position pos, of collection name</li>
</ul>
{!inc seccion/}
{/if}}
{%if Section4}}
<h2>Special builtin-function</h2>
<p>These are functions that work in a special way. These function are used to flow-control
    whatever it is in our templates, like loops or conditions. These functions start with
    \{%bif arg1 arg2...}}data...\{/bif}}</p>
<p>Available special builtin-functions are: </p>
<ul>
    <li>\{%if condition}}DATA\{/if}} : This will compute DATA only if <em>condition</em> is true.
    A condition can be:
    <ul>
        <li>a keyword: true if keyword exists and it's not empty and not "0"</li>
        <li>keyword[operator]"value" : will test keyword's value and "value" against
        operator. e.g.: myKeyword==32. Available operators are <, >, != (or <>), <=, >=, == (or =),
        but you can even create comparators in C++11 and use them here.</li>
    </ul>
    </li>
    <li>\{%collection var="collectionvar" [loops=X]}}DATA\{/collection}} : This will iterate over a collection (collectionvar)
    of elements and parse data as many times as loops are done. Loops can be a number, but if not specified,
    loops all collection elements. Inside a loop, some variables are created and updated:
    <ul>
        <li>collectionvar._totalLines : total elements in collection</li>
        <li>collectionvar._totalIterations : number of iterations (loops)</li>
        <li>collectionvar._last : 1 if it's the last iteration, 0 otherwise</li>
        <li>collectionvar._even : 1 if it's an even iteration (iterations start from 0)</li>
        <li>collectionvar._lineNumber : current line number</li>
    </ul>
    All other variables inside the collection may be accesed via: collectionvar.variable keyword
    </li>
</ul>
{!inc seccion/}
{/if}}
{%if Section5}}
<h2>Examples</h2>
<p>For a little practise, let's do:</p>
<pre>
#include <iostream>
#include <string>
#include "silicon.h"

using namespace std;

string includeCss (Silicon* s, Silicon::StringMap args, std::string input)
{
  auto file=args.find("file");
  if (file==args.end())
    return "";

  return "<link href=""+file->second+"" rel="stylesheet" type="text/css">";
}

int main(int argc, char* argv[])
{
  int sections[6] = {1, 0, 1, 1, 1, 1};z
  for (int i=1; i<argc; ++i)
   {
     if (i>6)
    break;
      sections[i-1] = (atoi(argv[i])!=0);
    }

  Silicon::setGlobalKeyword("ProjectTitle", "Silicon Example");
  Silicon::setGlobalKeyword("Author", "Gaspar Fernández");
  Silicon::setGlobalKeyword("AuthorEmail", "gaspar.fernandez@totaki.com");
  Silicon t = Silicon::createFromFile("sample1.html", "views/");

  t.setLayout("sample_layout.html");
  t.setKeyword("PageTitle", "Main");
  for (int i=0; i<6; ++i)
   t.setKeyword("Section"+to_string(i), to_string(sections[i]));
 t.setFunction("includeCss", includeCss);

 try
   {
     cout << t.render()<<std::endl;
   }
 catch (SiliconException &e)
   {
     cout << "Exception!!! "<<e.what() << std::endl;
   }
}
</pre>
{!inc seccion/}
{/if}}

{%if !seccion}}You chose not to show any section{/if}}

sample_layout.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
  <head>
    <title>{{PageTitle}} - {{ProjectTitle}}</title>
    <meta name="author" content="{{Author}}">
    {!includeCss file="layout.css"/}
  </head>

  <body>
    <div id="header">
        {!block template="sample_header.html"/}
    </div>
    <div id="content">
    {{contents}}
    </div>
    <div id="footer">
    <address>
      <a href="mailto:{{AuthorEmail}}">{{Author}}</a>,
    </address>
    <p>Generated on {!date format="%Y-%m-%d %H:%M:%S"/}</p>
    </div>
    </address>
  </body>
</html>

sample_header.html:

1
<h1>{{PageTitle}} - {{ProjectTitle}}</h1>

sample.cc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <string>
#include "silicon.h"

using namespace std;

string includeCss (Silicon* s, Silicon::StringMap args, std::string input)
{
  auto file=args.find("file");
  if (file==args.end())
    return "";

  return "<link href=""+file->second+"" rel="stylesheet" type="text/css">";
}

int main(int argc, char* argv[])
{
  int sections[6] = {1, 0, 1, 1, 1, 1};

  for (int i=1; i<argc; ++i)
    {
      if (i>6)
    break;
      sections[i-1] = (atoi(argv[i])!=0);
    }

  std::cout << "Enabled sections: ";
  for (int i=0; i<6; ++i)
    cout << sections[i]<<"\t";
  cout << endl;

  Silicon::setGlobalKeyword("ProjectTitle", "Silicon Example");
  Silicon::setGlobalKeyword("Author", "Gaspar Fernández");
  Silicon::setGlobalKeyword("AuthorEmail", "gaspar.fernandez@totaki.com");
  Silicon t = Silicon::createFromFile("sample1.html", "views/");
  //  t.setBasePath("views/");
  t.setLayout("sample_layout.html");
  t.setKeyword("PageTitle", "Main");
  for (int i=0; i<6; ++i)
    t.setKeyword("Section"+to_string(i), to_string(sections[i]));
  t.setFunction("includeCss", includeCss);

  try
    {
      cout << t.render()<<std::endl;
    }
  catch (SiliconException &e)
    {
      cout << "Exception!!! "<<e.what() << std::endl;
    }
}

En el código fuente podemos seleccionar a traǘes de los argumentos de entrada las secciones que queremos representar, por ejemplo, ejecutando:

$ ./sample 1 1 1 0 0 0

o

$ ./sample 1 0 1 0 1

Si falta un valor, se leerá el valor por defecto de la variable.

Tras ello, se establecen los valores de las palabras clave, se define el layout y la función includeCss. El resultado se obtendrá cuando Silicon parsee el fichero de plantilla, luego parseará el layout y colocará cada cosa en su sitio. El contenido de la plantilla principal una vez procesado irá en la posición en la que se encuentra {{contents}} en el layout.

Además, en la plantilla principal, podremos ver que se pregunta si Section0, Section1,…,Section5 existen para mostrarlas. También vemos que hemos escapado código de palabras clave o funciones de Silicon dentro de una plantilla para que no se procese (con \). Y si se ha elegido no mostrar ninguna sección, también lo detectará la propia plantilla.

Un ejemplo un poco más avanzado

Para este tipo de cosas, también tenemos una pequeña extensión llamada SiliconWeb, que incorpora funciones para incluir CSS y JS, así como para hacer el render de todo el JS de la web en un punto fijo de la misma independientemente de dónde se genere ese código. Esto, lo podemos utilizar, por ejemplo para el render de todos los Javascripts al final de la página, incluso cuando nos demos cuenta de que lo tenemos que incluir durante la representación de un bloque independiente.
Como podemos ver en este ejemplo, muy parecido al anterior pero con algunas cosas más para aprovechar nuevas incorporaciones de SiliconWeb:
sample1.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
{%if _siliconWeb}}
{!set _renderResources=0/}
{/if}}
{%iffun includeJs}}
{!includeJs file="myscript.js"/}
{/iffun}}
{%if Section0}}
{%iffun directJs}}
{!directJs}}
alert("Hello world!");
{/directJs}}
{/iffun}}
<h2>What we can do?</h2>
<ul>
    <li>Use templates as output for programs in C++11</li>
    <li>Templates may use keywords, collections and functions</li>
    <li>Use layouts as main template containing another templates.
    Just like a web page has a base design and a particular view
    for each page inside.</li>
    <li>Global keyword/function definitions to use it in all instances.</li>
    <li>No library requirements</li>
    <li>You can use it wherever you want. MIT licensed.</li>
</ul>
{!inc seccion/}
{/if}}
{%if Section1}}
<h2>Keywords</h2>
<p>Keywords are tags like <strong>{{</strong>word<strong>}}</strong> where
    <i>word</i> is a special token we choose and which will be replaced with
    a variable, or text we specify in our C++ code.</p>
<p>Silicon has also some pre-defined keywords as:</p>
<ul>
    <li>SiliconVersion : This library version. (e.g: {{SiliconVersion}})</li>
    <li>DS : Current directory separator. (e.g: {{DS}})</li>
</ul>
<p>From C++ you can define keywords by calling:</p>
<ul>
    <li>setKeyword(string keyword, string value) : for local keywords (only this instance)</li>
    <li>Silicon::setGlobalKeyword(string keyword, string value) : for global keywords</li>
</ul>
{!inc seccion/}
{/if}}
{%if Section2}}
<h2>Functions</h2>
<p>Functions are tags like <strong>\{!</strong>word<strong>/}</strong> where
    <i>word</i> is a special token we choose and which will be replaced with
    a variable, or text we specify in our C++ code.</p>
<p>Functions <strong>must</strong> be closed, so they can contain nested data
    with text, keywords, functions, an so on. We have normal and auto-closed
    function tags:</p>
<ul>
    <li>Normal: \{!functionName}}Data...text, and whatever\{/functionName}}</li>
    <li>Auto-closed: \{!functionName/} (auto-closed have no data at all.</li>
</ul>
<p>Functions may also have arguments as pairs of keys=values to modify their
    behaviour, like this:</p>
<p>\{!functionName argument1=value1 argument2=value2.../}</p>
or even like this:
<p>\{!functionName argument1=value1 argument2=value2...}}
Data, tags, text, functions, and so...
    \{/functionName}}</p>
<p>Functions can be used when declaring keywords that now always will be shown
    and require some time to compute.</p>
<p>Silicon also have some pre-defined functions{%if Section6}}(for detailed information, see below{/if}}:</p>
<ul>
    <li>SiliconTotalKeywords : Gets total keywords at this moment. (e.g: {!SiliconTotalKeywords/})</li>
    <li>date : Gets the date. (e.g: {!date/})</li>
    <li>pwd : Gets current directory. (e.g: {!pwd/})</li>
    <li>block : inserts a new block from file</li>
    <li>set : sets a value for one or more keywords</li>
    <li>inc : increment the value of one or more keywords</li>
</ul>
<p>From C++ you can define functions by calling:</p>
<ul>
    <li>setFunction(string functionName, TemplateFunction function) : for local functions</li>
    <li>Silicon::setGlobalFunction(string functionName, TemplateFunction function) : for global functions</li>
</ul>
<p>These functions must have this prototype:</p>
<p>   string myfunction(Silicon* s, Silicon::StringMap options)</p>
<p>where StringMap is just a map&lt;string, string>, containing keys and values for
    all function arguments: key1=value1, key2=value2, and so.</p>
<p>Your function must return the string being concatenated to the template.</p>
{!inc seccion/}
{/if}}
{%if Section3}}
<h2>Collections</h2>
<p>Collections are special keywords storing a number of key-value pairs. In C++, they are represented by
vector of maps, so we can iterate over the vector (rows) and get column-value pairs inside. </p>
<p>In this version, collections are accessed only by calling \{%collection}} </p>
<p>In C++, collections can be included using:</p>
<ul>
    <li>addCollection(string name, vector&lt;map&lt;string, string>> v) : to input a whole vector</li>
    <li>addToCollection(string name, StringMap m) : To append a map to the collection</li>
    <li>long addToCollection(string name, long pos, string key, string value) : To insert one key, and one value to the position pos, of collection name</li>
</ul>
{!inc seccion/}
{/if}}
{%if Section4}}
<h2>Special builtin-function</h2>
<p>These are functions that work in a special way. These function are used to flow-control
    whatever it is in our templates, like loops or conditions. These functions start with
    \{%bif arg1 arg2...}}data...\{/bif}}</p>
<p>Available special builtin-functions are: </p>
<ul>
    <li>\{%if condition}}DATA\{/if}} : This will compute DATA only if <em>condition</em> is true.
    A condition can be:
    <ul>
        <li>a keyword: true if keyword exists and it's not empty and not "0"</li>
        <li>keyword[operator]"value" : will test keyword's value and "value" against
        operator. e.g.: myKeyword==32. Available operators are <, >, != (or <>), <=, >=, == (or =),
        but you can even create comparators in C++11 and use them here.</li>
    </ul>
    </li>
    <li>\{%collection var="collectionvar" [loops=X]}}DATA\{/collection}} : This will iterate over a collection (collectionvar)
    of elements and parse data as many times as loops are done. Loops can be a number, but if not specified,
    loops all collection elements. Inside a loop, some variables are created and updated:
    <ul>
        <li>collectionvar._totalLines : total elements in collection</li>
        <li>collectionvar._totalIterations : number of iterations (loops)</li>
        <li>collectionvar._last : 1 if it's the last iteration, 0 otherwise</li>
        <li>collectionvar._even : 1 if it's an even iteration (iterations start from 0)</li>
        <li>collectionvar._lineNumber : current line number</li>
    </ul>
    All other variables inside the collection may be accesed via: collectionvar.variable keyword
    </li>
</ul>
{!inc seccion/}
{/if}}
{%if Section5}}
<h2>Examples</h2>
<p>For a little practise, let's do:</p>
<pre>
#include <iostream>
#include <string>
#include "silicon.h"

using namespace std;

string includeCss (Silicon* s, Silicon::StringMap args, std::string input)
{
  auto file=args.find("file");
  if (file==args.end())
    return "";

  return "<link href=""+file->second+"" rel="stylesheet" type="text/css">";
}

int main(int argc, char* argv[])
{
  int sections[6] = {1, 0, 1, 1, 1, 1};z
  for (int i=1; i<argc; ++i)
   {
     if (i>6)
    break;
      sections[i-1] = (atoi(argv[i])!=0);
    }

  Silicon::setGlobalKeyword("ProjectTitle", "Silicon Example");
  Silicon::setGlobalKeyword("Author", "Gaspar Fernández");
  Silicon::setGlobalKeyword("AuthorEmail", "gaspar.fernandez@totaki.com");
  Silicon t = Silicon::createFromFile("sample1.html", "views/");

  t.setLayout("sample_layout.html");
  t.setKeyword("PageTitle", "Main");
  for (int i=0; i<6; ++i)
   t.setKeyword("Section"+to_string(i), to_string(sections[i]));
 t.setFunction("includeCss", includeCss);

 try
   {
     cout << t.render()<<std::endl;
   }
 catch (SiliconException &e)
   {
     cout << "Exception!!! "<<e.what() << std::endl;
   }
}
</pre>
{!inc seccion/}
{/if}}

{%if !seccion}}You chose not to show any section{/if}}

{%if _siliconWeb}}
<h2>Sample list</h2>
{!insert samplelist text="Element 1" link="#" /}
{!insert samplelist text="Element 2" link="#"/}
{!insert samplelist text="Element 3" link="javascript:void(0)"/}
{!insert samplelist text="Element 4" link="http://localhost/"/}
{!insert samplelist text="Element 5" link="http://totaki.com/" /}
{%iffun list}}{!list collection=samplelist uselink=1 class="sampleclas" /}{/iffun}}
{/if}}

sample_layout.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
  <head>
    <title>{{PageTitle}} - {{ProjectTitle}}</title>
    <meta name="author" content="{{Author}}">
    {!includeCss file="layout.css"/}
    {%iffun renderCss}}
    {!renderCss comments=1/}
    {/iffun}}
  </head>

  <body>
    <div id="header">
        {!block template="sample_header.html"/}
    </div>
    <div id="content">
    {{contents}}
    </div>
    <div id="footer">
    <address>
      <a href="mailto:{{AuthorEmail}}">{{Author}}</a>,
    </address>
    <p>Generated on {!date format="%Y-%m-%d %H:%M:%S"/}</p>
    </div>
    </address>
    {%iffun renderJs}}
    {!renderJs comments=1/}
    {/iffun}}
  </body>
</html>

sample.cc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <string>
#include "silicon.h"
#include "siliconweb.h"

using namespace std;

int main(int argc, char* argv[])
{
  int sections[6] = {1, 0, 1, 1, 1, 1};

  for (int i=1; i<argc; ++i)
    {
      if (i>6)
    break;
      sections[i-1] = (atoi(argv[i])!=0);
    }

  std::cout << "Enabled sections: ";
  for (int i=0; i<6; ++i)
    cout << sections[i]<<"\t";
  cout << endl;

  Silicon::setGlobalKeyword("ProjectTitle", "Silicon Example");
  Silicon::setGlobalKeyword("Author", "Gaspar Fernández");
  Silicon::setGlobalKeyword("AuthorEmail", "gaspar.fernandez@totaki.com");
  SiliconWeb::load();       /* Load web extensions */

  Silicon t = Silicon::createFromFile("sample1.html", "views/");
  t.setKeyword("_baseURL", "/");
  SiliconWeb::cssUrl("css");
  //  t.setBasePath("views/");
  t.setLayout("sample_layout.html");
  t.setKeyword("PageTitle", "Main");
  for (int i=0; i<6; ++i)
    t.setKeyword("Section"+to_string(i), to_string(sections[i]));

  try
    {
      cout << t.render()<<std::endl;
    }
  catch (SiliconException &e)
    {
      cout << "Exception!!! "<<e.what() << std::endl;
    }
}

En este caso, al haber incluido SiliconWeb, basta con llamar a:

1
SiliconWeb::load()

Para cargar todas las funciones y keywords a modo global. Si nos fijamos en el HTML resultante, veremos que la ruta de los CSS será _baseURL concatenado con cssURL, por lo que podemos despreocuparnos de las rutas de todos los CSS. Archivos que podemos incluir tanto desde el código en C++ como desde los archivos de plantilla, ya que, a veces los CSS deberán incluirse cuando se haga el render de un bloque. Además, SiliconWeb incorpora estas funciones que podemos llamar dentro de las plantillas:

  • includeCss : que incluye un CSS en el HTML resultante
  • includeJs : incluye un archivo Javascript
  • directJs : incluye código Javascript personalizado
  • renderCss : hace un render de todas las inclusiones de CSS
  • renderJs : hace un render de todo los Javascripts que se hayan incluido y de los Javascripts inline.
  • insert : inserta un elemento dentro de una lista
  • list : Crea una lista HTML con los elementos de la colección especificada

Esto nos puede brindar muchas posibilidades a nuestros programas en C++, para crear salidas HTML fácilmente.

Colaboración

Esta biblioteca podéis utilizarla como queráis para lo que queráis, sin garantías “as is”, podéis crear trabajos derivados, romperla, destrozarla y reconstuirla. Eso sí, aunque no es obligatorio, agradecería que me enviaseis cambios, correcciones, bugs, etc.
Hay partes del código que bien podrían ser reescritas, otras deberán ser mejoradas y actualizadas, pero poco a poco. Si te animas, haz un fork del proyecto Silicon en github y échale un ojo.

Foto principal: Michael Podger

También podría interesarte....

Leave a Reply