Added a `bit_flags` attribute to enum declarations that 1<<N every value.
authorWouter van Oortmerssen <wvo@google.com>
Thu, 17 Jul 2014 22:12:37 +0000 (15:12 -0700)
committerWouter van Oortmerssen <wvo@google.com>
Mon, 21 Jul 2014 23:40:39 +0000 (16:40 -0700)
Change-Id: Ib9ec0cb3ddec60b1ca124eaf815fb1ae0cc53e1c
Tested: on Windows and Linux
Bug: 16186562

docs/html/md__schemas.html
docs/source/Schemas.md
src/idl_gen_text.cpp
src/idl_parser.cpp
tests/MyGame/Example/Color.go
tests/MyGame/Example/Color.java
tests/MyGame/Example/Monster.go
tests/MyGame/Example/Monster.java
tests/monster_test.fbs
tests/monster_test_generated.h
tests/test.cpp

index 6f1c6f6..85381e7 100644 (file)
@@ -123,6 +123,7 @@ root_type Monster;
 <li><code>deprecated</code> (on a field): do not generate accessors for this field anymore, code should stop using this data.</li>
 <li><code>original_order</code> (on a table): since elements in a table do not need to be stored in any particular order, they are often optimized for space by sorting them to size. This attribute stops that from happening.</li>
 <li><code>force_align: size</code> (on a struct): force the alignment of this struct to be something higher than what it is naturally aligned to. Causes these structs to be aligned to that amount inside a buffer, IF that buffer is allocated with that alignment (which is not necessarily the case for buffers accessed directly inside a <code>FlatBufferBuilder</code>).</li>
+<li><code>bit_flags</code> (on an enum): the values of this field indicate bits, meaning that any value N specified in the schema will end up representing 1&lt;&lt;N, or if you don't specify values at all, you'll get the sequence 1, 2, 4, 8, ...</li>
 </ul>
 <h2>Gotchas</h2>
 <h3>Schemas and version control</h3>
index debab67..4a02179 100755 (executable)
@@ -188,6 +188,10 @@ Current understood attributes:
     these structs to be aligned to that amount inside a buffer, IF that
     buffer is allocated with that alignment (which is not necessarily
     the case for buffers accessed directly inside a `FlatBufferBuilder`).
+-   `bit_flags` (on an enum): the values of this field indicate bits,
+    meaning that any value N specified in the schema will end up
+    representing 1<<N, or if you don't specify values at all, you'll get
+    the sequence 1, 2, 4, 8, ...
 
 ## Gotchas
 
index f35e884..fc833c1 100644 (file)
@@ -100,7 +100,8 @@ static void EscapeString(const String &s, std::string *_text) {
           text += c;
         } else {
           auto u = static_cast<unsigned char>(c);
-          text += "\\x" + IntToStringHex(u);
+          text += "\\x";
+          text += IntToStringHex(u);
         }
         break;
     }
index 436d0bc..29b328c 100644 (file)
@@ -629,10 +629,10 @@ void Parser::ParseEnum(bool is_union) {
     std::string dc = doc_comment_;
     Expect(kTokenIdentifier);
     auto prevsize = enum_def.vals.vec.size();
-    auto &ev = *new EnumVal(name, static_cast<int>(
-                      enum_def.vals.vec.size()
-                        ? enum_def.vals.vec.back()->value + 1
-                        : 0));
+    auto value = enum_def.vals.vec.size()
+      ? enum_def.vals.vec.back()->value + 1
+      : 0;
+    auto &ev = *new EnumVal(name, value);
     if (enum_def.vals.Add(name, &ev))
       Error("enum value already exists: " + name);
     ev.doc_comment = dc;
@@ -647,6 +647,15 @@ void Parser::ParseEnum(bool is_union) {
     }
   } while (IsNext(','));
   Expect('}');
+  if (enum_def.attributes.Lookup("bit_flags")) {
+    for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+         ++it) {
+      if (static_cast<size_t>((*it)->value) >=
+           SizeOf(enum_def.underlying_type.base_type) * 8)
+        Error("bit flag out of range of underlying integral type");
+      (*it)->value = 1 << (*it)->value;
+    }
+  }
 }
 
 void Parser::ParseDecl() {
index f4cc633..289a7b0 100644 (file)
@@ -3,7 +3,7 @@
 package Example
 
 const (
-       ColorRed = 0
-       ColorGreen = 1
-       ColorBlue = 2
+       ColorRed = 1
+       ColorGreen = 2
+       ColorBlue = 8
 )
index 44738ea..8144e41 100755 (executable)
@@ -3,8 +3,8 @@
 package MyGame.Example;
 
 public class Color {
-  public static final byte Red = 0;
-  public static final byte Green = 1;
-  public static final byte Blue = 2;
+  public static final byte Red = 1;
+  public static final byte Green = 2;
+  public static final byte Blue = 8;
 };
 
index 16894f1..2e35ab6 100644 (file)
@@ -80,7 +80,7 @@ func (rcv *Monster) Color() int8 {
        if o != 0 {
                return rcv._tab.GetInt8(o + rcv._tab.Pos)
        }
-       return 2
+       return 8
 }
 
 func (rcv *Monster) TestType() byte {
@@ -200,7 +200,7 @@ func MonsterAddHp(builder *flatbuffers.Builder, hp int16) { builder.PrependInt16
 func MonsterAddName(builder *flatbuffers.Builder, name flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(3, flatbuffers.UOffsetT(name), 0) }
 func MonsterAddInventory(builder *flatbuffers.Builder, inventory flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(5, flatbuffers.UOffsetT(inventory), 0) }
 func MonsterStartInventoryVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(1, numElems) }
-func MonsterAddColor(builder *flatbuffers.Builder, color int8) { builder.PrependInt8Slot(6, color, 2) }
+func MonsterAddColor(builder *flatbuffers.Builder, color int8) { builder.PrependInt8Slot(6, color, 8) }
 func MonsterAddTestType(builder *flatbuffers.Builder, testType byte) { builder.PrependByteSlot(7, testType, 0) }
 func MonsterAddTest(builder *flatbuffers.Builder, test flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(8, flatbuffers.UOffsetT(test), 0) }
 func MonsterAddTest4(builder *flatbuffers.Builder, test4 flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(9, flatbuffers.UOffsetT(test4), 0) }
index dcb7699..a6c584a 100755 (executable)
@@ -17,7 +17,7 @@ public class Monster extends Table {
   public String name() { int o = __offset(10); return o != 0 ? __string(o) : null; }
   public byte inventory(int j) { int o = __offset(14); return o != 0 ? bb.get(__vector(o) + j * 1) : 0; }
   public int inventoryLength() { int o = __offset(14); return o != 0 ? __vector_len(o) : 0; }
-  public byte color() { int o = __offset(16); return o != 0 ? bb.get(o + bb_pos) : 2; }
+  public byte color() { int o = __offset(16); return o != 0 ? bb.get(o + bb_pos) : 8; }
   public byte testType() { int o = __offset(18); return o != 0 ? bb.get(o + bb_pos) : 0; }
   public Table test(Table obj) { int o = __offset(20); return o != 0 ? __union(obj, o) : null; }
   public Test test4(int j) { return test4(new Test(), j); }
@@ -41,7 +41,7 @@ public class Monster extends Table {
   public static void addName(FlatBufferBuilder builder, int nameOffset) { builder.addOffset(3, nameOffset, 0); }
   public static void addInventory(FlatBufferBuilder builder, int inventoryOffset) { builder.addOffset(5, inventoryOffset, 0); }
   public static void startInventoryVector(FlatBufferBuilder builder, int numElems) { builder.startVector(1, numElems); }
-  public static void addColor(FlatBufferBuilder builder, byte color) { builder.addByte(6, color, 2); }
+  public static void addColor(FlatBufferBuilder builder, byte color) { builder.addByte(6, color, 8); }
   public static void addTestType(FlatBufferBuilder builder, byte testType) { builder.addByte(7, testType, 0); }
   public static void addTest(FlatBufferBuilder builder, int testOffset) { builder.addOffset(8, testOffset, 0); }
   public static void addTest4(FlatBufferBuilder builder, int test4Offset) { builder.addOffset(9, test4Offset, 0); }
index 9e6d444..caf6946 100755 (executable)
@@ -2,7 +2,7 @@
 
 namespace MyGame.Example;
 
-enum Color:byte { Red = 0, Green, Blue = 2 }
+enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3 }
 
 union Any { Monster }  // TODO: add more elements
 
index e6beb77..e362bd5 100755 (executable)
@@ -9,17 +9,17 @@ namespace MyGame {
 namespace Example {
 
 enum {
-  Color_Red = 0,
-  Color_Green = 1,
-  Color_Blue = 2,
+  Color_Red = 1,
+  Color_Green = 2,
+  Color_Blue = 8,
 };
 
 inline const char **EnumNamesColor() {
-  static const char *names[] = { "Red", "Green", "Blue", nullptr };
+  static const char *names[] = { "Red", "Green", "", "", "", "", "", "Blue", nullptr };
   return names;
 }
 
-inline const char *EnumNameColor(int e) { return EnumNamesColor()[e]; }
+inline const char *EnumNameColor(int e) { return EnumNamesColor()[e - Color_Red]; }
 
 enum {
   Any_NONE = 0,
@@ -85,7 +85,7 @@ struct Monster : private flatbuffers::Table {
   int16_t hp() const { return GetField<int16_t>(8, 100); }
   const flatbuffers::String *name() const { return GetPointer<const flatbuffers::String *>(10); }
   const flatbuffers::Vector<uint8_t> *inventory() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(14); }
-  int8_t color() const { return GetField<int8_t>(16, 2); }
+  int8_t color() const { return GetField<int8_t>(16, 8); }
   uint8_t test_type() const { return GetField<uint8_t>(18, 0); }
   const void *test() const { return GetPointer<const void *>(20); }
   const flatbuffers::Vector<const Test *> *test4() const { return GetPointer<const flatbuffers::Vector<const Test *> *>(22); }
@@ -131,7 +131,7 @@ struct MonsterBuilder {
   void add_hp(int16_t hp) { fbb_.AddElement<int16_t>(8, hp, 100); }
   void add_name(flatbuffers::Offset<flatbuffers::String> name) { fbb_.AddOffset(10, name); }
   void add_inventory(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory) { fbb_.AddOffset(14, inventory); }
-  void add_color(int8_t color) { fbb_.AddElement<int8_t>(16, color, 2); }
+  void add_color(int8_t color) { fbb_.AddElement<int8_t>(16, color, 8); }
   void add_test_type(uint8_t test_type) { fbb_.AddElement<uint8_t>(18, test_type, 0); }
   void add_test(flatbuffers::Offset<void> test) { fbb_.AddOffset(20, test); }
   void add_test4(flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) { fbb_.AddOffset(22, test4); }
@@ -150,7 +150,7 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
    int16_t hp = 100,
    flatbuffers::Offset<flatbuffers::String> name = 0,
    flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory = 0,
-   int8_t color = 2,
+   int8_t color = 8,
    uint8_t test_type = 0,
    flatbuffers::Offset<void> test = 0,
    flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4 = 0,
index b3dcab3..c28be7d 100644 (file)
@@ -477,6 +477,7 @@ void ErrorTest() {
   TestError("enum X:float {}", "underlying");
   TestError("enum X:byte { Y, Y }", "value already");
   TestError("enum X:byte { Y=2, Z=1 }", "ascending");
+  TestError("enum X:byte (bit_flags) { Y=8 }", "bit flag out");
   TestError("table X { Y:int; } table X {", "datatype already");
   TestError("struct X (force_align: 7) { Y:int; }", "force_align");
   TestError("{}", "no root");