Is there a way to wrap protocol buffers in Objective-C and still use inheritance?

I want to use protocol buffers in an iOS project. I try not to turn the entire project into an Objective-C ++ fiasco, so I want to combine the protobuf C ++ classes into Objective-C. I have dozens of protobuf posts, and although I did it successfully one class at a time, ideally I would like to use inheritance to minimize duplicate code. I am new to Objective-C and I have not used what I knew little about C ++ for 10 years, so this was basically an upset exercise. The following is an example of how I wrapped one message.

The code

.proto:

message MessageA { optional string value = 1; } 

MessageAWrapper.h:

 #import <Foundation/Foundation.h> @interface MessageAWrapper : NSObject @property (nonatomic) NSString *value; + (id)fromString:(NSString *)string; - (NSString *)serialize; @end 

MessageAWrapper.mm:

 #import "MessageA.h" #import "message.pb.h" @interface MessageAWrapper () @property (nonatomic) MessageA *message; @end @implementation MessageAWrapper - (id)init { self = [super init]; if (self) { self.message = new MessageA(); } return self; } - (void)dealloc { delete self.message; self.message = NULL; } - (NSString *)value { return [NSString stringWithUTF8String:self.message->value().c_str()]; } - (void)setValue:(NSString *)value { self.message->set_value([value UTF8String]); } - (NSString *)serialize { std::string output; self.message->SerializeToString(&output); return [NSString stringWithUTF8String:output.c_str()]; } + (id)fromString:(NSString *)string { MessageA *message = new MessageA(); message->ParseFromString([string UTF8String]); MessageAWrapper *wrapper = [[MessageAWrapper alloc] init]; wrapper.message = message; return wrapper; } @end 

goal

There is a lot of code here that will be repeated dozens of times, in which the only option is the type of the wrapped class ( init , dealloc , serialize , fromString ), so ideally I would instead put it in the parent class ProtobufMesssage . Unfortunately, I was unable to get this to work because I cannot find a way for the parent class to know the class that its children use, which is required, for example, in init and fromString .

Things I Took

  • structure
  • class of patterns
  • invalid *

Obstacles I faced

  • can't find a way to store class / type reference
  • cannot have headers or C ++ code in the .h file (since this requires the entire project to be Objective-C ++)
  • the difficulty of storing links to parent protobuf messages ( Message or MessageLite ), because they are abstract

As I said, I have very little understanding of C ++ or Objective-C; most of my experience is related to much higher-level languages ​​such as Python and Java (although I basically understand basic C things like pointers).

Perhaps this is not even possible? Am I approaching him incorrectly or do not see something obvious? Any help is appreciated. Thanks.

+6
source share
2 answers

I don't know anything about C ++ at all, but can you declare the Objective-C property as Message * ?

You have already separated C ++ code from the header by declaring a property in the .mm file, the problem you will encounter is the instance methods called the compiler ( value() and set_value() ), and only valid methods for subclassing. This can help use the Reflection class to get and set fields by their name. Here is an excerpt from Google message.h showing this:

 Message* foo = new Foo; const Descriptor* descriptor = foo->GetDescriptor(); const FieldDescriptor* text_field = descriptor->FindFieldByName("text"); assert(text_field != NULL); assert(text_field->type() == FieldDescriptor::TYPE_STRING); assert(text_field->label() == FieldDescriptor::LABEL_OPTIONAL); const Reflection* reflection = foo->GetReflection(); assert(reflection->GetString(foo, text_field) == "Hello World!"); 

You can create instance methods of Objective-C -objectForKey: and -setObject:forKey: which typecheck and get or set a value (confused, the key in the case of MessageAWrapper will be @"value" ). Your subclasses did not even have to know the C ++ code.

You can also separate the creator function in the -init method and +fromString: into something like +_createNewInstance ;

 +(Message*)_createNewInstance{ return new MessageA(); } 

letting your MessageWrapper subclasses reuse all code except creating a C ++ object.

0
source

While Objective-C has very powerful introspection capabilities, C ++ is more limited. You have RTTI (runtime type information), but it's not even as powerful as a copy of Objective C.

However, this may be sufficient. In your Objective C ++ class, you can find the type of your message object using the typeid operator:

 if( (typeid(self.message) == typed(foo)){ //doSomething else if( (typeid(self.message) == typed(bar)){ // doSomething else } 

Perhaps the best option is to add a different level of indirection. Create an Objective-C class hierarchy that wraps all of your C ++ buffer classes, and then create another Objective-C that uses these classes (possibly as delegates). I think this may be the best option. Use C ++ only for these unavoidable cases.

Good luck

0
source

All Articles