嗨!每一个,我都有类似这个问题:
关于节俭功能返回列表
当列表具有"可选"修饰符时,thrift C++服务器将始终将其作为null/undef返回.
此外,如果可选列表包含的结构,任何结构的字段,都不能设置为"可选",或者将返回null/undef值.
删除所有"可选"修饰符后,一切正常.
谁能告诉我为什么我不能在列表前使用"可选"?
这是节俭文件:
namespace cpp thrifttest namespace perl thrifttest struct Pair { 1: optional string a, 2: optional string b } struct Result { 1: optional liststrList, 2: optional list pairList } service Test { Result listTest() }
这是C++代码(服务器):
#include "gen-cpp/Test.h" #include#include #include #include #include using namespace std; using namespace ::apache::thrift; using namespace ::apache::thrift::protocol; using namespace ::apache::thrift::transport; using namespace ::apache::thrift::server; using boost::shared_ptr; using namespace ::thrifttest; class TestHandler : virtual public TestIf { public: TestHandler() { // Your initialization goes here } void listTest(Result& _return) { _return.strList.push_back("Test"); _return.strList.push_back("one level list"); cout << "strList size: " << _return.strList.size() << endl; Pair pair; pair.a = "Test"; pair.b = "two level list"; _return.pairList.push_back(pair); cout << "pairList size: " << _return.pairList.size() << endl; printf("call listTest\n"); } }; int main(int argc, char **argv) { int port = 9595; shared_ptr handler(new TestHandler()); shared_ptr processor(new TestProcessor(handler)); shared_ptr serverTransport(new TServerSocket(port)); shared_ptr transportFactory(new TBufferedTransportFactory()); shared_ptr protocolFactory(new TBinaryProtocolFactory()); TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory); server.serve(); return 0; }
这是perl代码(客户端):
#!/usr/bin/perl use v5.12; use warnings; use autodie; use utf8; use Data::Dumper; use lib 'gen-perl'; use thrifttest::Test; use thrifttest::Constants; use thrifttest::Types; use Thrift; use Thrift::BinaryProtocol; use Thrift::Socket; use Thrift::BufferedTransport; my $socket = new Thrift::Socket('localhost', 9595); my $transport = new Thrift::BufferedTransport($socket, 1024, 1024); my $protocol = new Thrift::BinaryProtocol($transport); my $client = new thrifttest::TestClient($protocol); eval { $transport->open(); my $result = $client->listTest; say Dumper($result); $transport->close(); }; say $@ if $@;
C++服务器输出:
strList size: 2 pairList size: 1 call listTest
perl客户端输出:
$VAR1 = bless( { 'pairList' => undef, 'strList' => undef }, 'thrifttest::Result' );
PS:我的开发环境是CentOS 7,GCC 4.8.3,Perl 5.16,节俭0.9.3
我错了,这不是一个真正的错误,它只是一些不幸的设计,并非真正的万无一失,并允许用户制作Dumb Things™.问题在于optional
运算符的语义以及如何为C++实现它.
让我们知道我们有这件IDL:
struct Xtruct2 { 1: i8 byte_thing, 2: Xtruct struct_thing, 3: i32 i32_thing }
生成的代码,特别是write()
方法,如下所示:
uint32_t Xtruct2::write(::apache::thrift::protocol::TProtocol* oprot) const { //... xfer += oprot->writeFieldBegin("struct_thing", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->struct_thing.write(oprot); xfer += oprot->writeFieldEnd(); //... }
如果我们现在修改IDL并添加说明optional
符:
struct Xtruct2 { 1: i8 byte_thing, 2: optional Xtruct struct_thing, 3: i32 i32_thing }
生成的代码看起来略有不同:
uint32_t Xtruct2::write(::apache::thrift::protocol::TProtocol* oprot) const { //... if (this->__isset.struct_thing) { xfer += oprot->writeFieldBegin("struct_thing", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->struct_thing.write(oprot); xfer += oprot->writeFieldEnd(); } //... }
节俭有三种requiredness的:required
,optional
和默认.如果既未指定required
也optional
未指定后者,则隐式假设后者(这就是为什么没有特定关键字用于默认要求).阅读和编写这些领域的语义如下:
requiredness write field? read field? ---------------------------------------------------------------------- required always always, must be present (default) always if present, may be missing optional only if set if present, may be missing
因此optional
,与默认值相比,更改的是write方法的行为.始终写入默认字段时,optional
只会有条件地写入字段.这是通过__isset
标志来检查的,标志由一个bitset组成,每个字段不包含一位required
.如果设置了相应的位标志__isset
,则可以使用字段值.如果不存在位标志,则字段值尚未初始化,因此不应使用.
到目前为止,这不会是一个很大的问题.但是有一个陷阱,你设法击中:默认和optional
字段可以在C++中访问和使用,即使没有设置位标志因为它们就在那里.在默认要求的情况下,这不是什么大问题:您分配您的值,并且由于该字段始终被写入,基本上没有任何不好的事情发生.当字段被反序列化时,字段的位标志被设置(参见生成的代码).
但是,当您选择加入时,事情会发生变化optional
:现在突然您有责任通过__isset
直接访问或通过生成的setter方法正确设置标志,在我们的例子中这个:
void Xtruct2::__set_struct_thing(const Xtruct& val) { this->struct_thing = val; __isset.struct_thing = true; }
我的假设是不应该访问一个尚未设置的可选字段,但事实证明这似乎是设计的.我仍然认为这里的设计有点太容易出错.