问题描述:
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).