thrift 源码学习 一
1. thrift代码整体流程
1.1. 入口函数main
- 如果输入gen,则添加到generator_strings中;初始化各个全局类型变量
- parse(program,null)解析程序
- generate(program,generator_strings)生成目标代码
- 释放全局变量
作者有意思的代码注释:(*^__^*)
// Clean up. Who am I kidding... this program probably orphans heap memory // all over the place, but who cares because it is about to exit and it is // all referenced and used by this wacky parse tree up until now anyways.
1.2. 解析函数 parse
- 常量不能是void类型
- inucde方式,yyparse词法分析;解析的结果放到t_program* parogram这个参数中
- program->get_includes: thrift.yy中
1.3. 代码生成 generate
- 先得到所有的include文件,依次调用generate
- 对每种指定的目标语言:
- get_generator(program,options)
- 参数options是语言参数,':'前是语言名,后面是参数值,每个参数通过','分割,参数值用'='连接
- get_generator_map,得到所有的语言生成器名称,然后找到从options中得到的语言
- get_generator(program, parsed_options, options) parsed_options是刚才解析options得到的键值对应的数组
- generate_program
- init_generator
- 初始化文件路径
- 初始化文件:xxxType,及其头部信息,php有<?php
- generate_enum
- 类名:final class xxx {
- 常量:const
- 静态结构体:static public $__names = array(xxx => xxx)
- generate_typedef php中不存在类型定义,所以这里函数没有设置
- generate_forward_declaration php中不需要声明结构、联合体等,所以也没有这部分代码
- generate_xception/generate_struct
- 类的声明,包括公共变量、静态变量
- 构造函数、getName函数
- read函数、write函数
- _validateForWrite/Read函数,其中包括抛出异常的代码
- generate_consts
- 定义final类,静态保护变量、函数
- generate_service
- 生成服务类
-
生成客户端类dump_docstrings(program)
2. 客户端、服务端数据不一致问题
2.1. 两端定义的field标识不同
代码在generate_enum部分可以找到对应:生成xx_type文件中的构造函数中结构定义代码
void t_php_generator::generate_php_struct_spec(ofstream& out, t_struct* tstruct) { ...... const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); indent(out) << (*m_iter)->get_key() << " => array(" << endl; indent_up(); out <<indent() << "'var' => '" << (*m_iter)->get_name() << "'," << endl; generate_php_type_spec(out, t); indent(out) << ")," << endl; indent_down(); } .......
根据上面代码,生成如下php代码
public function __construct($vals=null) { if (!isset(self::$_TSPEC)) { self::$_TSPEC = array( 1 => array( 'var' => 'query', 'type' => TType::STRING, ), 2 => array( 'var' => 'cityid', 'type' => TType::I32, ), ...... } }
thrift的sever与client参数在传递过程中,是按照thrift文件中的field的顺序放入,field顺序是按照每个元素前面标识的大小排序:
bool t_struct::append(t_field* elem) { typedef members_type::iterator iter_type; std::pair<iter_type, iter_type> bounds = std::equal_range( members_in_id_order_.begin(), members_in_id_order_.end(), elem, t_field::key_compare() ); ...... members_.push_back(elem); ...... return true; } struct t_field::key_compare { bool operator()(t_field const * const & a, t_field const * const & b) { return a->get_key() < b->get_key(); } };
2.2. 没有定义这个域的标识参数
找到thrift.yy文件,这里面根据flex&bison定义程序
Field: CaptureDocText FieldIdentifier FieldRequiredness FieldType FieldReference tok_identifier FieldValue XsdOptional XsdNillable XsdAttributes TypeAnnotations CommaOrSemicolonOptional { pdebug("tok_int_constant : Field -> FieldType tok_identifier"); if ($2.auto_assigned) { pwarning(1, "No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", $6); if (g_strict >= 192) { yyerror("Implicit field keys are deprecated and not allowed with -strict"); exit(1); } } validate_simple_identifier($6); $$ = new t_field($4, $6, $2.value); ...... }