ecpp - template-language for tntnet (8)
Ecpp is the template-language used by the tntnet-system to generate
A template consists of normal content (normally html-data) enriched
with special tags, which trigger some special handling.
One ecpp-file is compiled into a C++-class. The C++-class is placed
into the namespace component. A ecpp-file compiled into a C++-class is
called component. The name of the class is the basename of the file.
request, reply, qparam
Each component has 3 parameters: request, reply and qparam.
request holds information about the client-request like http-headers
and the url, but also additional parameters specified in the config-
file tntnet.conf(7). The type of request is tnt::HttpRequest.
reply receives the answer from the component. The component can set
additional http-headers here, set cookies and - most important -
generate output. The most important methods here are reply.out() and
reply.sout(). Both return a std::ostream, which receives the output of
the component. reply.sout() has a filter installed, which translates
some characters, whith special meanings in html to the corresponding
html-entities. The characters are <, >, &, " and ’. This is useful for
printing values from variables to the html-code.
qparam holds the query-parameters parsed from GET- or POST-parameters
or received from other components. The type of qparam is
cxxtools::query_params. Normally you use a <%args>-block to specify
the parameters, but there are special cases, where it is useful to
access these directly.
Each component has a unique name. The name is composed from the class-
name, the character ’@’ and the name of the shared library, it is
located. Components can have internal subcomponents. The name of the
internal subcomponent is appended to the classname separated by a dot
special rule for line feeds after a </%something>-tag
A line feed immediately after a closing tag for all <%something>-blocks
are ignored. Hence blocks followed immediately one after another does
not generate white space in output, which is often undesirable.
Error-handling is done by exception. Tntnet catches all exceptions
thrown by components and handles them properly. Exceptions must be
derived from std::exception. Exceptions derived from tnt::HttpError,
are handled separately. They carry a http-return-code, which is sent
to the client. Other exceptions derived from std::exception, result in
a http-error code 500 (Internal Server Error).
<$ expr $>
Print expressions expr to the outputstream. The characters <, >, &, "
and ’, which have special meanings in html, are translated to the
<$$ expr $>
Print expressions expr without translating characters with special
meaning in html to html-entities to the output-stream.
<? cond ? expr ?>
Conditional output. Print expression expr to the outputstream, if cond
evaluates to true. Characters with special meaning in html are
translated to the corresponding html-entities.
<& component [ arguments ] >
Call the specified component. The output of the component is printed
into the outputstream. If the component-name does not start with a
letter, the ecpp-compiler treats it as a expression, which returns the
name of the component. You must surround the expression in brackets,
if it contains spaces.
The arguments-part specify the parameters, the component will receive.
Arguments are names-value-pairs separated by ’=’. They are put in the
qparam-parameter of the component and are normally declared in the
<%args>-block. Values can be specified in 3 forms:
As a plain word without spaces
As a string enclosed in quotation marks
As a expression enclosed in brackets
A single plain word in the argumentlist is treated as a variable of
type cxxtools::query_params and a copy is passed to the component.
Other parameters are added to this copy. If you want to pass all
parameters of the current component put the variable qparam as a plain
word in the argument list.
Closing-tag for a component-call. When components are called, this
closing-tag might occur later. The code in <%close>-block is placed
C++-inline-processing-block. The code in this block is copied into the
A linefeed after the closing tag is not ignored.
Comment-block. Everything in this block is ignored.
<%application [ scope="component|page|global"] >...</%application>
Variables defined here, have the lifetime of the application.
Application-scope is automatically locked.
Defines GET- or POST-parameters recieved by the component.
Each argument has a name and optionally a defaul-value. The default-
value is delimited by ’=’ from the name. A single argument-definition
followed by a semicolon (;). In the component a variable with the same
name of type std::string is defined, which receives the value.
A argument-name can be prefixed by a type-definition. The ecpp-
compiler generates code, which tries to convert the value with the
input-stream-operator. This means, that each type, which can be read
from a input-stream (std::istream) can be used. If the argument can’t
be converted, a exception is thrown.
Argumentnames can be postfixed by empty square-brackets. This defines
a std::vector with the specified type or std::string, if no type is
specified. This way multiple values with the same name can be
received. If a type is specified, each value is converted to the
Code in these tags is placed into the calling component, when a closing
tag </&component> is found.
The <%close> receives the same parameters like the corresponding normal
Often webapplications need some configuration like database-names or
login-information to the database. These configuratioin-variables can
be read from the tntnet.conf. Variablenames ended with a semicolon are
defined as static std::string-variables and filled from tntnet.conf. A
variable can be prepended by a type. The value from tntnet.conf is then
converted with a std::istream.
You can also specify a default value by appending a ’=’ and the value
to the variable.
dburl = "sqlite:db=mydbfile.sqlite";
int maxvalue = 10;
dburl = "postgresql:dbname=mydb";
C++-processing-block. The code between these tags are copied into the
A linefeed after the closing tag is ignored.
Defines a internal subcomponent with the name name, which can be called
like other components.
Comment-block. Everything in this block is ignored.
A linefeed after the closing tag is ignored.
Encloses a block of text-data, which is to be translated. See ecppl(1)
and ecppll(1) for details.
The specified file is read and compiled.
Defines parameter received from calling components. In contrast to
query-parameters these variables can be of any type. The syntax (and
the underlying technology) is the same like in scoped variables. See
the description about scoped variables to see how to define parameters.
The main difference is, that a parameter variable has no scope, since
the parameter is always local to the component.
Defines C++-code, which is placed outside the C++-class and outside the
namespace-definition. This is a good place to define #include-
<%request [ scope="component|page|global"] >...</%request>
Define request-scope variables. Variables defined here, has the
lifetime of the request.
<%session [ scope="component|page|global"] >...</%session>
Variables defined here, has the lifetime of the session.
Sessions are identified with cookies. If a <%session>-block is defined
somewhere in a component, a session-cookie is sent to the client.
Sessions are automatically locked.
<%thread [ scope="component|page|global"] >...</%thread>
Variables defined here, has the lifetime of the thread. Each thread
has his own instance of these variables.
Thread-scope-variables do not need to be locked at all, because they
are only valid in the current thread.
Scoped variables are c++-variables, whose lifetime is handled by
tntnet. These variables has a lifetime and a scope. The lifetime is
defined by the tag, used to declare the variable and the scope is
passed as a parameter to the tag.
There are 5 different lifetimes for scoped variables:
The variable is valid in the current request. The tag is
The variable is valid in the application. The tag is
<%application>. The application is specified by the
shared-library of the top-level component.
The variable is valid for the current session. The tag is
<%session>. If at least session-variable is declared in
the current request, a session-cookie is sent to the
thread The variable is valid in the current thread. The tag is
param The variable receives parameters. The tag is <%param>.
And 3 scopes:
The variable is only valid in the same component. This
is the default scope.
page The variable is shared between the components in a single
ecpp-file. You can specify multiple internal
subcomponents in a %def-block. Variables, defined in
page-scope are shared between these subcomponents.
global Variables are shared between all components. If you
define the same variable with global-scope in different
components, they must have the same type. This is
achieved most easily defining them in a separate file and
include them with a <%include>-block.
Variables are automatically locked as needed.
If you use session-variables, tntnet ensures, that all requests
of the same session are serialized. If you use application-
variables, tntnet serializes all requests to the same
application-scope. Request- and thread-scope variables do not
need to be locked at all, because they are not shared between
Syntax of scoped variables
Scoped variables are declared with exactly the same syntax as normal
variables in c++-code. They can be of any type and are instantiated,
when needed. Objects, which do not have default constructors, need to
be specified with proper constructor-parameters in brackets or
separated by ’=’. The parameters are only used, if the variable need to
be instantiated. This means, that paramters to e.g. application-scope
variables are only used once. When the same component is called later
in the same or another request, the parameters are not used any more.
unsigned count = 0;
Specify a application-specific global variable, which is initialized
Specify a variable with a user-defined type, which holds the state of
Specify a persistent databaseconnection, which is initialized, when
first needed and hold for the lifetime of the current thread. This
variable may be used in other components.
This manual page was written by Tommi Mäkitalo <email@example.com>.