New generator options for oneofs: allow skipping or generating as normal 'optional...
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>
Sun, 11 Jan 2015 17:38:05 +0000 (19:38 +0200)
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>
Sun, 11 Jan 2015 17:45:16 +0000 (19:45 +0200)
The behaviour with no_unions:true is the same as of nanopb 0.3.1 and earlier.

docs/migration.rst
generator/nanopb_generator.py
generator/proto/nanopb.proto

index 5f7246f..d5ded64 100644 (file)
@@ -11,6 +11,25 @@ are included, in order to make it easier to find this document.
 
 .. contents ::
 
+Nanopb-0.3.2 (2015-01-xx)
+=========================
+
+Add support for OneOfs
+----------------------
+**Rationale:** Previously nanopb did not support the *oneof* construct in
+*.proto* files. Those fields were generated as regular *optional* fields.
+
+**Changes:** OneOfs are now generated as C unions. Callback fields are not
+supported inside oneof and generator gives an error.
+
+**Required actions:** The generator option *no_unions* can be used to restore old
+behaviour and to allow callbacks to be used. To use unions, one change is
+needed: use *which_xxxx* field to detect which field is present, instead
+of *has_xxxx*. Compare the value against *MyStruct_myfield_tag*.
+
+**Error indications:** Generator error: "Callback fields inside of oneof are
+not supported". Compiler error: "Message" has no member named "has_xxxx".
+
 Nanopb-0.3.0 (2014-08-26)
 =========================
 
index 3f309a4..0d7be49 100755 (executable)
@@ -591,7 +591,7 @@ class OneOf(Field):
     def add_field(self, field):
         if field.allocation == 'CALLBACK':
             raise Exception("Callback fields inside of oneof are not supported"
-                            + " (field %s)" % field.fullname)
+                            + " (field %s)" % field.name)
 
         field.union_name = self.name
         field.rules = 'ONEOF'
@@ -652,13 +652,20 @@ class Message:
     def __init__(self, names, desc, message_options):
         self.name = names
         self.fields = []
-        self.oneofs = []
+        self.oneofs = {}
+        no_unions = []
 
         if hasattr(desc, 'oneof_decl'):
-            for f in desc.oneof_decl:
-                oneof = OneOf(self.name, f)
-                self.oneofs.append(oneof)
-                self.fields.append(oneof)
+            for i, f in enumerate(desc.oneof_decl):
+                oneof_options = get_nanopb_suboptions(desc, message_options, self.name + f.name)
+                if oneof_options.no_unions:
+                    no_unions.append(i) # No union, but add fields normally
+                elif oneof_options.type == nanopb_pb2.FT_IGNORE:
+                    pass # No union and skip fields also
+                else:
+                    oneof = OneOf(self.name, f)
+                    self.oneofs[i] = oneof
+                    self.fields.append(oneof)
 
         for f in desc.field:
             field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
@@ -666,8 +673,11 @@ class Message:
                 continue
 
             field = Field(self.name, f, field_options)
-            if hasattr(f, 'oneof_index') and f.HasField('oneof_index'):
-                self.oneofs[f.oneof_index].add_field(field)
+            if (hasattr(f, 'oneof_index') and
+                f.HasField('oneof_index') and
+                f.oneof_index not in no_unions):
+                if f.oneof_index in self.oneofs:
+                    self.oneofs[f.oneof_index].add_field(field)
             else:
                 self.fields.append(field)
         
@@ -734,7 +744,7 @@ class Message:
         '''Returns number of required fields inside this message'''
         count = 0
         for f in self.fields:
-            if f not in self.oneofs:
+            if not isinstance(f, OneOf):
                 if f.rules == 'REQUIRED':
                     count += 1
         return count
@@ -742,7 +752,7 @@ class Message:
     def count_all_fields(self):
         count = 0
         for f in self.fields:
-            if f in self.oneofs:
+            if isinstance(f, OneOf):
                 count += len(f.fields)
             else:
                 count += 1
index e830ec2..a1b2493 100644 (file)
@@ -53,6 +53,9 @@ message NanoPBOptions {
   
   // Skip this message
   optional bool skip_message = 6 [default = false];
+
+  // Generate oneof fields as normal optional fields instead of union.
+  optional bool no_unions = 8 [default = false];
 }
 
 // Extensions to protoc 'Descriptor' type in order to define options