Total Knowledge: CPPSERV: Documentation: Writing taglibs

CPPSERV: Documentation: Writing taglibs


Home Projects Jobs Clientele Publishing Contact

Writing Taglibs

Introduction

Taglib is a special extension to CSP, which allows one to define custom tags.

Currently CPPSERV CSP parser only supports compile-time taglibs. In other words, taglib is loaded while parsing the CSP document, and it affects generated C++ (as opposed to run-time taglibs, which would load at run-time, and would affect generated output. Currently CSP taglibs only support separate opening and closing tags.

Taglib example

Writing compile-time CSP taglib is pretty straight-forward. The basic steps are:

  • #include servlet/taglib/CompileTimeTaglib.h
  • #include servlet/taglib/Generator.h
  • Define a class derived from servlet::taglib::Generator for each tag you want to implement
  • Override doStartTag and doEndTag functions
  • Call EXPORT_COMPILE_TIME_TAG macro with your prefix, tag name, and class name as arguments. This should be done for each tag.
  • Call COMPILE_TIME_TAGLIB macro with the prefix of your tags as an argument.

doStartTag and doEndTag functions have access to body, header, and member ostream pointers for outputting generated code.
Note: pointers (specifically std::ostream*). This means one has to use something like *body<<"/*generated comment*/;

  • body: the body of the generated servlet's service function.
  • header: the header portion of the generated servlet. It is meant for generating #include directives, and such
  • member: the place where member declarations in generated servlet can be placed.

Here is actual code:

	#include <servlet/taglib/Generator.h>
	#include <servlet/taglib/CompileTimeTaglib.h>
	using namespace servlet;
	using namespace taglib;

	static inline void get_attr(const Generator::attribs_t& attribs,
		const std::string& name, std::string& dest, const std::string& tag,
		const std::string& default_val = std::string())
	{
		attribs_t::const_iterator it = attribs.find(name);
		if(it!=attribs.end())
			dest=it->second;
		else if(!default_val.empty())
			dest = default_val;
		else
		throw std::runtime_error(tag +" requires attribute "+name);
	}

	class ListIteratorTag: public Generator
	{
	public:
		std::string m_list;
	public:
		ListIteratorTag(const std::string& name)
			: Generator(name)
		{}
		virtual void doStartTag(const attribs_t& attribs);
		virtual void doEndTag();
	};

	void ListIteratorTag::doStartTag(const Generator::attribs_t& attribs)
	{
		std::string var, type;
		get_attr(attribs, "list", m_list, "mylib:foreach_list");
		get_attr(attribs, "var", var, "mylib:foreach_list");
		get_attr(attribs, "type", type, "mylib:foreach_list");
		*body<<"for("<<type<<"::iterator "<<var<<"="<<m_list<<".begin();";
		*body<<var<<"!="<<m_list<<".end(); "<<var<<"++) {\n\t";
	}

	void ListIteratorTag::doEndTag()
	{
		*body<<"}\n";
		*body<<"/* end mylib:foreach_list: "<<m_list<<" */"<<std::endl;
	}
	EXPORT_COMPILE_TIME_TAG(mylib, foreach_list, ListIteratorTag)
	COMPILE_TIME_TAGLIB(mylib)
	

Let's take the example apart, piece by piece. The code above defines taglib with mylib prefix, and a single tag: foreach_list. The tag uses three attributes: list, var, and type. Resulting output code iterates over the list, executing tag body, with var as iterator variable. For example:

	<%
	 std::list<int> testlist;
	 testlist.push_back(1);
	 testlist.push_back(10);
	 testlist.push_back(100);
	%>
	<UL>
	<mylib:foreach_list list="testlist" var="val" type="std::list<int>">
	 <LI> Value: <%=*val%>
	</mylib:foreach_list>
	
When the page is executed, this should generate something like:
  • Value: 1
  • Value: 10
  • Value: 100

First we define class ListIteratorTag, derived from Generator. Objects of this class will be instantiated every time <mylib:foreach_list> is encountered, and destroyed when </mylib:foreach_list> is found.

std::string m_list;
function doStartTag takes list of tag attributes which CSP parser found, but doEndTag does not. This means we have to save information used by doEndTag() in member variables.

std::string var, type
Declare variables to hold attribute values.

Next three lines of doStartTag function retrieve list, var, and type attributes, throwing an exception, in case any of them is missing.

Next, simple for loop is generated, as well as opening brace. These are sent to the output stream representing the service function body.

doEndTag function simply closes the brace and outputs a comment, specifying that foreach_list for given list has been closed here (for convenience of debugging, in case there is a syntax error in the contained block).

EXPORT_COMPILE_TIME_TAG(mylib, foreach_list, ListIteratorTag)
COMPILE_TIME_TAGLIB(mylib)
These two lines register the tag and containing taglib.

SourceForge.net Logo

Authoright © Total Knowledge: 1999-2013