初识CPPCMS中的template概念

问题描述:

cppcms事实上一个mvc的web架构,其中一个重要组成部分就是view-html rendering系统。cppcms的网页是用特殊的template语言编写,编写后的网页可以由翻译器(cppcms_tmpl_cc)翻译成c++代码,最后可以由编译器编译成shared object。

通常情况下,cppcms template系统使用push model, 当用户应用程序准备好内容,将内容推送给web template系统用以渲染。所以mvc对应的角色如下:content -- model, application -- controller, view -- template system. 

上面的这段描述摘自tutorial-2(starting with templates)。如果编写较为复杂的网站,我们需要摸清template系统翻译后的c++代码是如何跟扮演model角色的content的c++代码进行耦合的)

 

问题解决:

 

1. 先根据tutorial-2中的示例代码,编写如下的template。

 

<% c++ #include "content.h" %>
<% skin my_skin %>
<% view message uses content::message %>
<% template render() %>
<html>
<body>
<h1><%= text %> World!</h1>
</body>
<html>
<% end template %>
<% end view %>
<% end skin %>

 

 2. model —— content.h的定义(非常简单,只是包含一个字符串数据的model定义,定义的结构体必须公有继承于base::content类)

 

#ifndef CONTENT_H
#define CONTENT_H

#include <cppcms/view.h>
#include <string>

namespace content  {
    struct message : public cppcms::base_content {
        std::string text;
    };
}

#endif

 

 3. controller —— hello-tmpl.cpp (my_hello_world application, 按照mvc模式,controller里必须关联model与view, 如何关联的呢?)

 

#include <cppcms/application.h>
#include <cppcms/applications_pool.h>
#include <cppcms/service.h>
#include <cppcms/http_response.h>
#include <cppcms/url_dispatcher.h>
#include <iostream>

#include "content.h"

class my_hello_world : public cppcms::application {
public:
    my_hello_world(cppcms::service &s) :
       cppcms::application(s)
    {
    }
    virtual void main(std::string /*url*/)
    {
        content::message c;  // uses model 'message', which is defined in 'content.h'.
        c.text=">>>Hello<<<";  // initialize member model data
        render("message",c);  // collabrate with model, and view, where view is a template name. 	
    }
};

在my_hello_world的main函数中,调用了render函数,该函数的接口定义如下

void cppcms::application::render(std::string template_name,
base_content &content)

/*		
Render a template template_name of default skin using content content.

Side effect requires: output stream for response class, causes all updated session data be saved and all headers be written. You can't change headers after calling this function.
*/

 根据此api,我们知道了为什么message必须继承于cppcms::base_content的原因,同时知道template的名称是message.然后,我们再看template是如何定义template的名称是message的。很显然,包含message关键字的语句,只有这么一句,即 <%view message uses content::message%>。这条语句实际上有两个语义

1. 定义了template name是message

2. 关联model数据 content::message

 

但是这个语句同样也给出了一些让人费解的关键词,比如view. 既然定义template name, 为什么不直接使用这样的语句<% template message uses content::message %>呢?view与template到底又是什么区别呢?在此语句后面的<% template render() %>句子中,template关键字到底是什么语义呢?其中render(),之前并未给出定义,为什么要加小括号?难道跟my_hello_world()的render() api有关系?skin关键字的语义又是什么?

 

上述的问题通过翻译后的c++代码或许能明朗些。

#include "content.h" 
namespace my_skin {
	struct message :public cppcms::base_view
	{
		content::message &content;
		message(std::ostream &_s,content::message &_content): cppcms::base_view(_s),content(_content)
		{
		}
		virtual void render() {
			out()<<"\n"
				"<html>\n"
				"<body>\n"
				"<h1>";
			out()<<cppcms::filters::escape(content.text);
			out()<<" World! </h1>\n"
				"</body>\n"
				"</html>\n"
				"";
		} // end of template render
	}; // end of class message
} // end of namespace my_skin

namespace {
 cppcms::views::generator my_generator; 
 struct loader { 
  loader() { 
   my_generator.name("my_skin");
   my_generator.add_view<my_skin::message,content::message>("message",true);
    cppcms::views::pool::instance().add(my_generator);
 }
 ~loader() {  cppcms::views::pool::instance().remove(my_generator); }
} a_loader;
} // anon 

 

回答上述问题:

1. view的语义是定义一个基类cppcms::base_view的子类,以及该子类所关联的model数据, 关联的model数据实际上被定义类该子类的成员变量。

2. template的语义是定义上述子类中的一个函数,后跟函数名(函数返回值实际上是该函数的第一个参数[输出流])。虽然render()这个函数名与my_hello_world application中的render()函数名称相同,但是毫无关系。

3. skin的语义是定义个一个命名空间。

 

这样,我个人觉得上述的render api定义,应该是view的名称,而不是模板名称。因为c++代码中,并没有template name这个概念。如果有,也是函数名称。

 

另外,需要注意的是翻译后c++代码中下面的部分,cppcms框架中定义了一个view池实例,池中包含cppcms::views::generator(view生成器),每个生成器(skin的名称,就是每个generator的名称)可以产生若干不同的view,通过调用add_view即可,并且加入view时,应给予一个一一对应名称。(猜想方便后续application的调用) 

template<typename View, typename Content>
void cppcms::views::generator::add_view(std::string const& view_name,
bool safe=true)

/*
Add a single view of type View that uses content of type Content Using name view_name.

If safe is true that dynamic cast is used to ensure that content has proper type otherwise static cast.

Usually used by templates generator
*/

 

结论:

 

1. struct class_name : public cppcms::base_content 定义model data

2. <% view view_name uses content::data %>定义cppcms::base_view的一个子类,content::data被声明为该子类的一个成员。

        3.<% template xxx() %> 定义2给出的子类中的一个函数,函数的返回中通常为一个输出流(即render后的html代码)

4. cppcms框架中定义个一个view池,view池中包含的对象是cppcms::views::generator对象,generator的名称是skin的名称,每个generator可以生成若干view。add_view<view_type, content>("view_name", true). 

你可能感兴趣的:(template)