完美之道,不在无可增加,而在无可删减。
分类: C/C++
2014-01-22 12:48:50
pb是必须选择requied或optional之一,如不没有标示,用proto编译是会报错的:
protoc –cpp_out ./ test.proto
test.proto:4:5: Expected “required”, “optional”, or “repeated”. 1 message Person { 2 required string name = 1; 3 required int32 id = 2; 4 string email = 3; 5 } |
但是thrift还有无标签类型数据,也是因为thrift支持了跟多的协议,而pb可以说只是支持一种数据传输协议。thrift的无标签字段如果没有赋值那么就是空的,在传输过程中也会继续传输该值,今天来分析一下。首先写一个测试的thrift文件来编译:
namespace cpp xlight
struct StUser { 1: required i32 userId; 2: optional string userName; 4: string language; } /// 编译 helight:rpc$thrift –gen cpp -out ./ test.thrift |
上面的测试文件可以看出,有些字段设置了requied,有些字段设置了optional,有些字段是什么也没有设置,就只是字段类型和字段名。先看看编译后的文件:test_types.h
typedef struct _StUser__isset {
_StUser__isset() : userName(false), language(false) {} bool userName; bool language; } _StUser__isset;
class StUser { public:
static const char* ascii_fingerprint; // = “76285C3D933C871361DFACF1222DDAAE”; static const uint8_t binary_fingerprint[16]; // = {0×76,0×28,0x5C,0x3D,0×93,0x3C,0×87,0×13,0×61,0xDF,0xAC,0xF1,0×22,0x2D,0xDA,0xAE};
StUser() : userId(0), userName(), language() { }
virtual ~StUser() throw() {}
int32_t userId; std::string userName; std::string language; _StUser__isset __isset;
void __set_userId(const int32_t val) { userId = val; }
void __set_userName(const std::string& val) { userName = val; __isset.userName = true; }
void __set_language(const std::string& val) { language = val; } bool operator == (const StUser & rhs) const { if (!(userId == rhs.userId)) return false; if (__isset.userName != rhs.__isset.userName) return false; else if (__isset.userName && !(userName == rhs.userName)) return false; if (!(language == rhs.language)) return false; return true; } bool operator != (const StUser &rhs) const { return !(*this == rhs); }
bool operator < (const StUser & ) const;
uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
}; |
可以看出thrift在cpp中把struct转换成了class,生成的代码中都把成员变量进行初始化设置,同时在代码中引入了另外一个结构体_StUser__isset 来记录这些字段的标签行为,对于requried的字段isset中没有bool变量标示,但是对于optional和无标签字段都有,然后我们再来看看它的序列化和反序列化函数—write和read函数。在test_types.cpp:
代码过多就不粘贴了:主要是在write的时候会判断optional是否被设置,如果没有被设置则不进行序列化。其它两种字段都会序列化。
if (this->__isset.userName) {
xfer += oprot->writeFieldBegin(“userName”, ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->userName); xfer += oprot->writeFieldEnd(); } |
在反序列化函数中:对requried字段做了标示,如果这个字段没有则会抛出异常,其它两个字段在反序列化之后都会设置其isset变量。从代码中可以看出thrift对requried的字段只是作了默认值处理,让它在数据结构初始化的时候就有值,避免在序列化的时候无值。而pb对这块的处理是如果不设置requried字段,程序运行会失败。
case 1:
if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->userId); isset_userId = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->userName); this->__isset.userName = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->language); this->__isset.language = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; 。。。 if (!isset_userId) throw TProtocolException(TProtocolException::INVALID_DATA); |