先打开xml模板文件并将文件内容读入一个名为xml的string:
std::ifstream ifs(argv[i]);
std::string xml((std::istreambuf_iterator<char>(ifs)),
std::istreambuf_iterator<char>());
获取不带扩展名的文件名filebase,以后作为namespace名:
path f(path(argv[i]).stem()); //boost 功能,只取文件名,不带路径和扩展名
std::string filebase = f.string();
根据xml的内容生成一个名为des的dynamic_templates_description对象:
mfast::dynamic_templates_description desc(xml.c_str(),
filebase.c_str(),
®istry);
dynamic_templates_description的构造函数如下,调用tinyxml2,创建一个XMLDoucument类的对象document用以解析xml内容。
dynamic_templates_description::dynamic_templates_description(const char* xml_content,
const char* cpp_ns,
template_registry* registry)
{
XMLDocument document;
if (document.Parse(xml_content) == 0) {
xml_parser::templates_builder builder(this, cpp_ns, registry);
document.Accept(&builder);
}
else {
BOOST_THROW_EXCEPTION(std::runtime_error("XML parse error"));
}
}
解析成功的话创建一个templates_builer类的对象builder用以初始化遍历document,该类继承自public XMLVisitor、boost::base_from_member和field_builder_base。Accept函数在tinyxml2中定义,对于不同的XML元素类型定义不同。对XMLDocument的定义如下,利用一对VisITEnter/VisitExit函数作为出入口。
bool XMLDocument::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
if ( visitor->VisitEnter( *this ) ) {
for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
if ( !node->Accept( visitor ) ) {
break;
}
}
}
return visitor->VisitExit( *this );
}
这里调用的应该是templates_builder的visit函数,定义如下。参数为XMLDocument的visitenter函数没有重载,直接返回TRUE。
然后调用参数为XMLElement的visitenter函数,代码如下。先获取element名称,如果是templates(xml文件根元素),则配置des的ns_、template_ns_和dictionary三个成员变量,然后返回TRUE,继续accept过程处理子节点。项目中templates节点的子节点为template,利用templates_builder和template元素创建field_builder类的对象builder,调用build函数,创建field。
bool
templates_builder::VisitEnter (const XMLElement & element, const XMLAttribute*)
{
const char* element_name = element.Name();
if (std::strcmp(element_name, "templates") == 0 ) {
definition_->ns_ = string_dup(get_optional_attr(element, "ns", ""), alloc());
resolved_ns_= string_dup(get_optional_attr(element, "templateNs", ""), alloc());
definition_->template_ns_ = resolved_ns_;
definition_->dictionary_ = string_dup(get_optional_attr(element, "dictionary", ""), alloc());
return true;
}
else if (strcmp(element_name, "define") == 0) {
const char* name = get_optional_attr(element, "name", 0);
const XMLElement* elem = element.FirstChildElement();
if (name && elem) {
field_builder builder(this, *elem, name);
builder.build();
}
}
else if (strcmp(element_name, "template") == 0) {
field_builder builder(this, element);
builder.build();
}
else if (strcmp(element_name, "view") == 0) {
view_info_builder builder(alloc());
const group_field_instruction* inst = dynamic_cast<const group_field_instruction*>(this->find_type(
get_optional_attr(element,"ns", resolved_ns_),
get_optional_attr(element,"reference", "")));
if (inst == 0)
BOOST_THROW_EXCEPTION(fast_static_error("Invalid view specification"));
definition_->view_infos_.push_back( builder.build(element, inst) );
}
return false;
}
bool
templates_builder::VisitExit (const XMLElement & element)
{
if (std::strcmp(element.Name(), "templates") == 0 ) {
typedef const template_instruction* const_template_instruction_ptr_t;
definition_->instructions_ = new (alloc())const_template_instruction_ptr_t[this->num_instructions()];
std::copy(templates_.begin(), templates_.end(), definition_->instructions_);
definition_->instructions_count_ = static_cast<uint32_t>(templates_.size());
}
return true;
}
field_builder的构造函数如下。第一个参数为field_builder_base类型的,调用的时候传入的为templates_builder,为其子类。
field_builder::field_builder(field_builder_base* parent,
const XMLElement& element)
: fast_xml_attributes(element.FirstAttribute())
, field_builder_base(parent->registry(),
parent->local_types())
, element_(element)
, parent_(parent)
{
}
其基类fast_xml_attributes利用element(此处为template类型)的第一个attribute初始化,该基类的构造函数如下。可以看到,根据此attribute的类型,设置fast_xml_attributes的相应属性,本项目中一般为name属性。
fast_xml_attributes(const XMLAttribute* attr)
{
name_ = 0;
id_ = 0;
ns_ = 0;
templateNs_ = 0;
dictionary_ = 0;
presence_ = 0;
charset_ = 0;
tag_ = 0;
decimal_place_ = 0;
set(attr);
}
void set(const XMLAttribute* attr)
{
while (attr != 0) {
const char* name = attr->Name();
if (std::strcmp(name, "name") == 0)
name_ = attr->Value();
else if (std::strcmp(name, "ns") == 0)
ns_ = attr->Value();
else if (std::strcmp(name, "templateNs") == 0)
templateNs_ = attr->Value();
else if (std::strcmp(name, "dictionary") == 0)
dictionary_ = attr->Value();
else if (std::strcmp(name, "id") == 0)
id_ = attr->Value();
else if (std::strcmp(name, "presence") == 0)
presence_ = attr->Value();
else if (std::strcmp(name, "charset") == 0)
charset_ = attr->Value();
else if (std::strcmp(name, "mfast:tag") == 0)
tag_ = attr->Value();
else if (std::strcmp(name, "decimalPlaces") == 0)
decimal_place_ = attr->Value();
attr = attr->Next();
}
}
下一步调用build过程,代码如下。首先获取节点名称template,然后从本类型或事先注册的map里面检索对应名称的instruction,这一步原理还不清楚。然后调用instruction的accept过程,实质是调用field_builder的visit函数,visit再调用相应类型的built函数,或者直接调用parent_->add_instruction过程,讲此过程注册进templates_builder,即注册进dynamic_templates_description。至此,完成一个template的解析。
void field_builder::build()
{
const field_instruction* prototype =
find_prototype(resolve_field_type(element_));
if (prototype) {
prototype->accept(*this, 0);
}
}
const char* field_builder::resolve_field_type(const XMLElement& element)
{
field_type_name_ = element.Name();
content_element_ = &element;
if (std::strcmp(field_type_name_, "field") == 0 ) {
content_element_ = element.FirstChildElement();
if (content_element_) {
field_type_name_ = content_element_->Name();
if (strcmp(field_type_name_, "type")==0) {
field_type_name_ = name_;
name_ = 0;
fast_xml_attributes::set(content_element_->FirstAttribute());
if (name_ == 0)
throw std::runtime_error("type element does not have a name");
std::swap(field_type_name_, name_);
}
else {
fast_xml_attributes::set(content_element_->FirstAttribute());
}
}
else {
throw std::runtime_error("field element must have a child element");
}
}
resolved_ns_ = ns_; //ns_继承自fast_xml_attributes,resolved_ns_继承自field_builder_base
if (resolved_ns_ == 0 && parent_) {
resolved_ns_ = parent_->resolved_ns();
}
return field_type_name_;
}
const field_instruction* field_builder::find_prototype(const char* type_name)
{
if (type_name == 0)
{
BOOST_THROW_EXCEPTION(fast_static_error("S1") << reason_info("no field type specified"));
}
const field_instruction* instruction = 0;
if (std::strcmp(type_name, "string") == 0) {
if (this->charset_ == 0)
this->charset_ = get_optional_attr(element_, "charset", "ascii");
if (std::strcmp(this->charset_, "unicode") == 0 )
{
static unicode_field_instruction prototype (operator_none,presence_mandatory,0,0,"",0, string_value_storage(), 0, "", "");
instruction = &prototype;
}
}
else if (std::strcmp(type_name, "templateRef") == 0)
{
return templateref_instruction::default_instruction()[0];
}
else if (std::strcmp(type_name, "typeRef") == 0 || std::strcmp(type_name, "length") == 0)
{
return 0;
}
if (instruction == 0) {
instruction = this->find_type(ns_, type_name);
}
if ( instruction == 0 )
{
BOOST_THROW_EXCEPTION(fast_static_error("S1") << reason_info((std::string("Invalid field type specified : ") + type_name)));
}
return instruction;
}