Oneof fields are like regular fields except all the fields in a oneof share memory, and at most one field can be set at the same time. Setting any member of the oneof automatically clears all the other members.
SampleMessagemessage;SubMessage*sub_message=message.mutable_sub_message();message.set_name("name");// Will delete sub_message
sub_message->set_...// Crashes here
if you Swap() two messages with oneofs, each will end up with the other's oneof case
Large Data Sets: Protocol Buffers are not designed to handle large messages. As a general rule of thumb, if you are dealing in messages larger than 1 megabyte each, it may be time to condider an alternate strategy.
syntax="proto2";packagetutorial;messagePerson{optionalstringname=1;optionalint32id=2;optionalstringemail=3;enumPhoneType{MOBILE=0;HOME=1;WORK=2;}messagePhoneNumber{optionalstringnumber=1;optionalPhoneTypetype=2[default=HOME];// set default value
}repeatedPhoneNumberphones=4;}messageAddressBook{repeatedPersonpeople=1;}
// name
inlineboolhas_name()const;inlinevoidclear_name();inlineconst::std::string&name()const;inlinevoidset_name(const::std::string&value);inlinevoidset_name(constchar*value);inline::std::string*mutable_name();// id
inlineboolhas_id()const;inlinevoidclear_id();inlineint32_tid()const;inlinevoidset_id(int32_tvalue);// email
inlineboolhas_email()const;inlinevoidclear_email();inlineconst::std::string&email()const;inlinevoidset_email(const::std::string&value);inlinevoidset_email(constchar*value);inline::std::string*mutable_email();// phones
inlineintphones_size()const;inlinevoidclear_phones();inlineconst::google::protobuf::RepeatedPtrField<::tutorial::Person_PhoneNumber>&phones()const;inline::google::protobuf::RepeatedPtrField<::tutorial::Person_PhoneNumber>*mutable_phones();inlineconst::tutorial::Person_PhoneNumber&phones(intindex)const;inline::tutorial::Person_PhoneNumber*mutable_phones(intindex);inline::tutorial::Person_PhoneNumber*add_phones();
As you can see, the getters have exactly the name as the field in lowercase, and the setter methods begin with set_. There are also has_ methods for each singular (required or optional) field which return true if that field has been set. Finally, each field has a clear_ method that un-sets the field back to its empty state.
While the numeric id field just has the basic accessor set described above, the name and email fields have a couple of extra methods because they're strings – a mutable_ getter that lets you get a direct pointer to the string, and an extra setter. Note that you can call mutable_email() even if email is not already set; it will be initialized to an empty string automatically. If you had a singular message field in this example, it would also have a mutable_ method but not a set_ method.
Repeated fields also have some special methods – if you look at the methods for the repeated phones field, you'll see that you can
check the repeated field's size (in other words, how many phone numbers are associated with this Person).
get a specified phone number using its index.
update an existing phone number at the specified index.
add another phone number to the message which you can then edit (repeated scalar types have an add that just lets you pass in the new value).
The generated code includes a PhoneType enum that corresponds to your .proto enum. You can refer to this type as Person::PhoneType and its values as Person::MOBILE, Person::HOME, and Person::WORK (the implementation details are a little more complicated, but you don't need to understand them to use the enum).
Each message class also contains a number of other methods that let you check or manipulate the entire message, including:
bool IsInitialized() const;: checks if all the required fields have been set.
string DebugString() const;: returns a human-readable representation of the message, particularly useful for debugging.
void CopyFrom(const Person& from);: overwrites the message with the given message's values.
void Clear();: clears all the elements back to the empty state.
#include<iostream>#include<fstream>#include<string>#include"addressbook.pb.h"usingnamespacestd;// This function fills in a Person message based on user input.
voidPromptForAddress(tutorial::Person*person){cout<<"Enter person ID number: ";intid;cin>>id;person->set_id(id);cin.ignore(256,'\n');cout<<"Enter name: ";getline(cin,*person->mutable_name());cout<<"Enter email address (blank for none): ";stringemail;getline(cin,email);if(!email.empty()){person->set_email(email);}while(true){cout<<"Enter a phone number (or leave blank to finish): ";stringnumber;getline(cin,number);if(number.empty()){break;}tutorial::Person::PhoneNumber*phone_number=person->add_phones();phone_number->set_number(number);cout<<"Is this a mobile, home, or work phone? ";stringtype;getline(cin,type);if(type=="mobile"){phone_number->set_type(tutorial::Person::MOBILE);}elseif(type=="home"){phone_number->set_type(tutorial::Person::HOME);}elseif(type=="work"){phone_number->set_type(tutorial::Person::WORK);}else{cout<<"Unknown phone type. Using default."<<endl;}}}// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
intmain(intargc,char*argv[]){// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;if(argc!=2){cerr<<"Usage: "<<argv[0]<<" ADDRESS_BOOK_FILE"<<endl;return-1;}tutorial::AddressBookaddress_book;{// Read the existing address book.
fstreaminput(argv[1],ios::in|ios::binary);if(!input){cout<<argv[1]<<": File not found. Creating a new file."<<endl;}elseif(!address_book.ParseFromIstream(&input)){cerr<<"Failed to parse address book."<<endl;return-1;}}// Add an address.
PromptForAddress(address_book.add_people());{// Write the new address book back to disk.
fstreamoutput(argv[1],ios::out|ios::trunc|ios::binary);if(!address_book.SerializeToOstream(&output)){cerr<<"Failed to write address book."<<endl;return-1;}}// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();return0;}
#include<iostream>#include<fstream>#include<string>#include"addressbook.pb.h"usingnamespacestd;// Iterates though all people in the AddressBook and prints info about them.
voidListPeople(consttutorial::AddressBook&address_book){for(inti=0;i<address_book.people_size();i++){consttutorial::Person&person=address_book.people(i);cout<<"Person ID: "<<person.id()<<endl;cout<<" Name: "<<person.name()<<endl;if(person.has_email()){cout<<" E-mail address: "<<person.email()<<endl;}for(intj=0;j<person.phones_size();j++){consttutorial::Person::PhoneNumber&phone_number=person.phones(j);switch(phone_number.type()){casetutorial::Person::MOBILE:cout<<" Mobile phone #: ";break;casetutorial::Person::HOME:cout<<" Home phone #: ";break;casetutorial::Person::WORK:cout<<" Work phone #: ";break;}cout<<phone_number.number()<<endl;}}}// Main function: Reads the entire address book from a file and prints all
// the information inside.
intmain(intargc,char*argv[]){// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;if(argc!=2){cerr<<"Usage: "<<argv[0]<<" ADDRESS_BOOK_FILE"<<endl;return-1;}tutorial::AddressBookaddress_book;{// Read the existing address book.
fstreaminput(argv[1],ios::in|ios::binary);if(!address_book.ParseFromIstream(&input)){cerr<<"Failed to parse address book."<<endl;return-1;}}ListPeople(address_book);// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();return0;}
syntax="proto3";optionjava_multiple_files=true;optionjava_package="io.grpc.examples.routeguide";optionjava_outer_classname="RouteGuideProto";optionobjc_class_prefix="RTG";packagerouteguide;// Interface exported by the server.
serviceRouteGuide{// A simple RPC.
//
// Obtains the feature at a given position.
//
// A feature with an empty name is returned if there's no feature at the given
// position.
rpcGetFeature(Point)returns(Feature){}// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpcListFeatures(Rectangle)returns(streamFeature){}// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpcRecordRoute(streamPoint)returns(RouteSummary){}// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpcRouteChat(streamRouteNote)returns(streamRouteNote){}}// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
messagePoint{int32latitude=1;int32longitude=2;}// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
messageRectangle{// One corner of the rectangle.
Pointlo=1;// The other corner of the rectangle.
Pointhi=2;}// A feature names something at a given point.
//
// If a feature could not be named, the name is empty.
messageFeature{// The name of the feature.
stringname=1;// The point where the feature is detected.
Pointlocation=2;}// A RouteNote is a message sent while at a given point.
messageRouteNote{// The location from which the message is sent.
Pointlocation=1;// The message to be sent.
stringmessage=2;}// A RouteSummary is received in response to a RecordRoute rpc.
//
// It contains the number of individual points received, the number of
// detected features, and the total distance covered as the cumulative sum of
// the distance between each point.
messageRouteSummary{// The number of points received.
int32point_count=1;// The number of known features passed while traversing the route.
int32feature_count=2;// The distance covered in metres.
int32distance=3;// The duration of the traversal in seconds.
int32elapsed_time=4;}
#include<algorithm>#include<chrono>#include<cmath>#include<iostream>#include<memory>#include<string>#include"helper.h"#include<grpc/grpc.h>#include<grpcpp/security/server_credentials.h>#include<grpcpp/server.h>#include<grpcpp/server_builder.h>#include<grpcpp/server_context.h>#ifdef BAZEL_BUILD
#include"examples/protos/route_guide.grpc.pb.h"#else
#include"route_guide.grpc.pb.h"#endif
usinggrpc::Server;usinggrpc::ServerBuilder;usinggrpc::ServerContext;usinggrpc::ServerReader;usinggrpc::ServerReaderWriter;usinggrpc::ServerWriter;usinggrpc::Status;usingrouteguide::Feature;usingrouteguide::Point;usingrouteguide::Rectangle;usingrouteguide::RouteGuide;usingrouteguide::RouteNote;usingrouteguide::RouteSummary;usingstd::chrono::system_clock;floatConvertToRadians(floatnum){returnnum*3.1415926/180;}// The formula is based on http://mathforum.org/library/drmath/view/51879.html
floatGetDistance(constPoint&start,constPoint&end){constfloatkCoordFactor=10000000.0;floatlat_1=start.latitude()/kCoordFactor;floatlat_2=end.latitude()/kCoordFactor;floatlon_1=start.longitude()/kCoordFactor;floatlon_2=end.longitude()/kCoordFactor;floatlat_rad_1=ConvertToRadians(lat_1);floatlat_rad_2=ConvertToRadians(lat_2);floatdelta_lat_rad=ConvertToRadians(lat_2-lat_1);floatdelta_lon_rad=ConvertToRadians(lon_2-lon_1);floata=pow(sin(delta_lat_rad/2),2)+cos(lat_rad_1)*cos(lat_rad_2)*pow(sin(delta_lon_rad/2),2);floatc=2*atan2(sqrt(a),sqrt(1-a));intR=6371000;// metres
returnR*c;}std::stringGetFeatureName(constPoint&point,conststd::vector<Feature>&feature_list){for(constFeature&f:feature_list){if(f.location().latitude()==point.latitude()&&f.location().longitude()==point.longitude()){returnf.name();}}return"";}classRouteGuideImplfinal:publicRouteGuide::Service{public:explicitRouteGuideImpl(conststd::string&db){routeguide::ParseDb(db,&feature_list_);}StatusGetFeature(ServerContext*context,constPoint*point,Feature*feature)override{feature->set_name(GetFeatureName(*point,feature_list_));feature->mutable_location()->CopyFrom(*point);returnStatus::OK;}StatusListFeatures(ServerContext*context,constrouteguide::Rectangle*rectangle,ServerWriter<Feature>*writer)override{autolo=rectangle->lo();autohi=rectangle->hi();longleft=(std::min)(lo.longitude(),hi.longitude());longright=(std::max)(lo.longitude(),hi.longitude());longtop=(std::max)(lo.latitude(),hi.latitude());longbottom=(std::min)(lo.latitude(),hi.latitude());for(constFeature&f:feature_list_){if(f.location().longitude()>=left&&f.location().longitude()<=right&&f.location().latitude()>=bottom&&f.location().latitude()<=top){writer->Write(f);}}returnStatus::OK;}StatusRecordRoute(ServerContext*context,ServerReader<Point>*reader,RouteSummary*summary)override{Pointpoint;intpoint_count=0;intfeature_count=0;floatdistance=0.0;Pointprevious;system_clock::time_pointstart_time=system_clock::now();while(reader->Read(&point)){point_count++;if(!GetFeatureName(point,feature_list_).empty()){feature_count++;}if(point_count!=1){distance+=GetDistance(previous,point);}previous=point;}system_clock::time_pointend_time=system_clock::now();summary->set_point_count(point_count);summary->set_feature_count(feature_count);summary->set_distance(static_cast<long>(distance));autosecs=std::chrono::duration_cast<std::chrono::seconds>(end_time-start_time);summary->set_elapsed_time(secs.count());returnStatus::OK;}StatusRouteChat(ServerContext*context,ServerReaderWriter<RouteNote,RouteNote>*stream)override{RouteNotenote;while(stream->Read(¬e)){std::unique_lock<std::mutex>lock(mu_);for(constRouteNote&n:received_notes_){if(n.location().latitude()==note.location().latitude()&&n.location().longitude()==note.location().longitude()){stream->Write(n);}}received_notes_.push_back(note);}returnStatus::OK;}private:std::vector<Feature>feature_list_;std::mutexmu_;std::vector<RouteNote>received_notes_;};voidRunServer(conststd::string&db_path){std::stringserver_address("0.0.0.0:50051");RouteGuideImplservice(db_path);ServerBuilderbuilder;builder.AddListeningPort(server_address,grpc::InsecureServerCredentials());builder.RegisterService(&service);std::unique_ptr<Server>server(builder.BuildAndStart());std::cout<<"Server listening on "<<server_address<<std::endl;server->Wait();}intmain(intargc,char**argv){// Expect only arg: --db_path=path/to/route_guide_db.json.
std::stringdb=routeguide::GetDbFileContent(argc,argv);RunServer(db);return0;}
#include<iostream>#include<memory>#include<string>#include<thread>#include<grpc/support/log.h>#include<grpcpp/grpcpp.h>#ifdef BAZEL_BUILD
#include"examples/protos/helloworld.grpc.pb.h"#else
#include"helloworld.grpc.pb.h"#endif
usinggrpc::Server;usinggrpc::ServerAsyncResponseWriter;usinggrpc::ServerBuilder;usinggrpc::ServerCompletionQueue;usinggrpc::ServerContext;usinggrpc::Status;usinghelloworld::Greeter;usinghelloworld::HelloReply;usinghelloworld::HelloRequest;classServerImplfinal{public:~ServerImpl(){server_->Shutdown();// Always shutdown the completion queue after the server.
cq_->Shutdown();}// There is no shutdown handling in this code.
voidRun(){std::stringserver_address("0.0.0.0:50051");ServerBuilderbuilder;// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address,grpc::InsecureServerCredentials());// Register "service_" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *asynchronous* service.
builder.RegisterService(&service_);// Get hold of the completion queue used for the asynchronous communication
// with the gRPC runtime.
cq_=builder.AddCompletionQueue();// Finally assemble the server.
server_=builder.BuildAndStart();std::cout<<"Server listening on "<<server_address<<std::endl;// Proceed to the server's main loop.
HandleRpcs();}private:// Class encompasing the state and logic needed to serve a request.
classCallData{public:// Take in the "service" instance (in this case representing an asynchronous
// server) and the completion queue "cq" used for asynchronous communication
// with the gRPC runtime.
CallData(Greeter::AsyncService*service,ServerCompletionQueue*cq):service_(service),cq_(cq),responder_(&ctx_),status_(CREATE){// Invoke the serving logic right away.
Proceed();}voidProceed(){if(status_==CREATE){// Make this instance progress to the PROCESS state.
status_=PROCESS;// As part of the initial CREATE state, we *request* that the system
// start processing SayHello requests. In this request, "this" acts are
// the tag uniquely identifying the request (so that different CallData
// instances can serve different requests concurrently), in this case
// the memory address of this CallData instance.
service_->RequestSayHello(&ctx_,&request_,&responder_,cq_,cq_,this);}elseif(status_==PROCESS){// Spawn a new CallData instance to serve new clients while we process
// the one for this CallData. The instance will deallocate itself as
// part of its FINISH state.
newCallData(service_,cq_);// The actual processing.
std::stringprefix("Hello ");reply_.set_message(prefix+request_.name());// And we are done! Let the gRPC runtime know we've finished, using the
// memory address of this instance as the uniquely identifying tag for
// the event.
status_=FINISH;responder_.Finish(reply_,Status::OK,this);}else{GPR_ASSERT(status_==FINISH);// Once in the FINISH state, deallocate ourselves (CallData).
deletethis;}}private:// The means of communication with the gRPC runtime for an asynchronous
// server.
Greeter::AsyncService*service_;// The producer-consumer queue where for asynchronous server notifications.
ServerCompletionQueue*cq_;// Context for the rpc, allowing to tweak aspects of it such as the use
// of compression, authentication, as well as to send metadata back to the
// client.
ServerContextctx_;// What we get from the client.
HelloRequestrequest_;// What we send back to the client.
HelloReplyreply_;// The means to get back to the client.
ServerAsyncResponseWriter<HelloReply>responder_;// Let's implement a tiny state machine with the following states.
enumCallStatus{CREATE,PROCESS,FINISH};CallStatusstatus_;// The current serving state.
};// This can be run in multiple threads if needed.
voidHandleRpcs(){// Spawn a new CallData instance to serve new clients.
newCallData(&service_,cq_.get());void*tag;// uniquely identifies a request.
boolok;while(true){// Block waiting to read the next event from the completion queue. The
// event is uniquely identified by its tag, which in this case is the
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
GPR_ASSERT(cq_->Next(&tag,&ok));GPR_ASSERT(ok);static_cast<CallData*>(tag)->Proceed();}}std::unique_ptr<ServerCompletionQueue>cq_;Greeter::AsyncServiceservice_;std::unique_ptr<Server>server_;};intmain(intargc,char**argv){ServerImplserver;server.Run();return0;}
#include<iostream>#include<memory>#include<string>#include<grpc/support/log.h>#include<grpcpp/grpcpp.h>#ifdef BAZEL_BUILD
#include"examples/protos/helloworld.grpc.pb.h"#else
#include"helloworld.grpc.pb.h"#endif
usinggrpc::Channel;usinggrpc::ClientAsyncResponseReader;usinggrpc::ClientContext;usinggrpc::CompletionQueue;usinggrpc::Status;usinghelloworld::Greeter;usinghelloworld::HelloReply;usinghelloworld::HelloRequest;classGreeterClient{public:explicitGreeterClient(std::shared_ptr<Channel>channel):stub_(Greeter::NewStub(channel)){}// Assembles the client's payload, sends it and presents the response back
// from the server.
std::stringSayHello(conststd::string&user){// Data we are sending to the server.
HelloRequestrequest;request.set_name(user);// Container for the data we expect from the server.
HelloReplyreply;// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContextcontext;// The producer-consumer queue we use to communicate asynchronously with the
// gRPC runtime.
CompletionQueuecq;// Storage for the status of the RPC upon completion.
Statusstatus;// stub_->PrepareAsyncSayHello() creates an RPC object, returning
// an instance to store in "call" but does not actually start the RPC
// Because we are using the asynchronous API, we need to hold on to
// the "call" instance in order to get updates on the ongoing RPC.
std::unique_ptr<ClientAsyncResponseReader<HelloReply>>rpc(stub_->PrepareAsyncSayHello(&context,request,&cq));// StartCall initiates the RPC call
rpc->StartCall();// Request that, upon completion of the RPC, "reply" be updated with the
// server's response; "status" with the indication of whether the operation
// was successful. Tag the request with the integer 1.
rpc->Finish(&reply,&status,(void*)1);void*got_tag;boolok=false;// Block until the next result is available in the completion queue "cq".
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or the cq_ is shutting down.
GPR_ASSERT(cq.Next(&got_tag,&ok));// Verify that the result from "cq" corresponds, by its tag, our previous
// request.
GPR_ASSERT(got_tag==(void*)1);// ... and that the request was completed successfully. Note that "ok"
// corresponds solely to the request for updates introduced by Finish().
GPR_ASSERT(ok);// Act upon the status of the actual RPC.
if(status.ok()){returnreply.message();}else{return"RPC failed";}}private:// Out of the passed in Channel comes the stub, stored here, our view of the
// server's exposed services.
std::unique_ptr<Greeter::Stub>stub_;};intmain(intargc,char**argv){// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint (in this case,
// localhost at port 50051). We indicate that the channel isn't authenticated
// (use of InsecureChannelCredentials()).
GreeterClientgreeter(grpc::CreateChannel("localhost:50051",grpc::InsecureChannelCredentials()));std::stringuser("world");std::stringreply=greeter.SayHello(user);// The actual RPC call!
std::cout<<"Greeter received: "<<reply<<std::endl;return0;}