1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// https://developers.google.com/protocol-buffers/ 4// 5// Redistribution and use in source and binary forms, with or without 6// modification, are permitted provided that the following conditions are 7// met: 8// 9// * Redistributions of source code must retain the above copyright 10// notice, this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above 12// copyright notice, this list of conditions and the following disclaimer 13// in the documentation and/or other materials provided with the 14// distribution. 15// * Neither the name of Google Inc. nor the names of its 16// contributors may be used to endorse or promote products derived from 17// this software without specific prior written permission. 18// 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31// Author: kenton@google.com (Kenton Varda) 32// Based on original Protocol Buffers design by 33// Sanjay Ghemawat, Jeff Dean, and others. 34 35#include <map> 36 37#include <google/protobuf/compiler/cpp/cpp_enum.h> 38#include <google/protobuf/compiler/cpp/cpp_helpers.h> 39#include <google/protobuf/io/printer.h> 40#include <google/protobuf/stubs/strutil.h> 41 42namespace google { 43namespace protobuf { 44namespace compiler { 45namespace cpp { 46 47namespace { 48// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value 49// is ::google::protobuf::kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the 50// generation of the GOOGLE_ARRAYSIZE constant. 51bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) { 52 int32 max_value = descriptor->value(0)->number(); 53 for (int i = 0; i < descriptor->value_count(); i++) { 54 if (descriptor->value(i)->number() > max_value) { 55 max_value = descriptor->value(i)->number(); 56 } 57 } 58 return max_value != ::google::protobuf::kint32max; 59} 60} // namespace 61 62EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, 63 const Options& options) 64 : descriptor_(descriptor), 65 classname_(ClassName(descriptor, false)), 66 options_(options), 67 generate_array_size_(ShouldGenerateArraySize(descriptor)) { 68} 69 70EnumGenerator::~EnumGenerator() {} 71 72void EnumGenerator::FillForwardDeclaration( 73 map<string, const EnumDescriptor*>* enum_names) { 74 if (!options_.proto_h) { 75 return; 76 } 77 (*enum_names)[classname_] = descriptor_; 78} 79 80void EnumGenerator::GenerateDefinition(io::Printer* printer) { 81 map<string, string> vars; 82 vars["classname"] = classname_; 83 vars["short_name"] = descriptor_->name(); 84 vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : ""); 85 86 printer->Print(vars, "enum $enumbase$ {\n"); 87 printer->Annotate("enumbase", descriptor_); 88 printer->Indent(); 89 90 const EnumValueDescriptor* min_value = descriptor_->value(0); 91 const EnumValueDescriptor* max_value = descriptor_->value(0); 92 93 for (int i = 0; i < descriptor_->value_count(); i++) { 94 vars["name"] = EnumValueName(descriptor_->value(i)); 95 // In C++, an value of -2147483648 gets interpreted as the negative of 96 // 2147483648, and since 2147483648 can't fit in an integer, this produces a 97 // compiler warning. This works around that issue. 98 vars["number"] = Int32ToString(descriptor_->value(i)->number()); 99 vars["prefix"] = (descriptor_->containing_type() == NULL) ? 100 "" : classname_ + "_"; 101 vars["deprecation"] = descriptor_->value(i)->options().deprecated() ? 102 " PROTOBUF_DEPRECATED" : ""; 103 104 if (i > 0) printer->Print(",\n"); 105 printer->Print(vars, "$prefix$$name$$deprecation$ = $number$"); 106 107 if (descriptor_->value(i)->number() < min_value->number()) { 108 min_value = descriptor_->value(i); 109 } 110 if (descriptor_->value(i)->number() > max_value->number()) { 111 max_value = descriptor_->value(i); 112 } 113 } 114 115 if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { 116 // For new enum semantics: generate min and max sentinel values equal to 117 // INT32_MIN and INT32_MAX 118 if (descriptor_->value_count() > 0) printer->Print(",\n"); 119 printer->Print(vars, 120 "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min,\n" 121 "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max"); 122 } 123 124 printer->Outdent(); 125 printer->Print("\n};\n"); 126 127 vars["min_name"] = EnumValueName(min_value); 128 vars["max_name"] = EnumValueName(max_value); 129 130 if (options_.dllexport_decl.empty()) { 131 vars["dllexport"] = ""; 132 } else { 133 vars["dllexport"] = options_.dllexport_decl + " "; 134 } 135 136 printer->Print(vars, 137 "$dllexport$bool $classname$_IsValid(int value);\n" 138 "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n" 139 "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n"); 140 141 if (generate_array_size_) { 142 printer->Print(vars, 143 "const int $prefix$$short_name$_ARRAYSIZE = " 144 "$prefix$$short_name$_MAX + 1;\n\n"); 145 } 146 147 if (HasDescriptorMethods(descriptor_->file(), options_)) { 148 printer->Print(vars, 149 "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n"); 150 // The _Name and _Parse methods 151 printer->Print( 152 vars, 153 "inline const ::std::string& $classname$_Name($classname$ value) {\n" 154 " return ::google::protobuf::internal::NameOfEnum(\n" 155 " $classname$_descriptor(), value);\n" 156 "}\n"); 157 printer->Print(vars, 158 "inline bool $classname$_Parse(\n" 159 " const ::std::string& name, $classname$* value) {\n" 160 " return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n" 161 " $classname$_descriptor(), name, value);\n" 162 "}\n"); 163 } 164} 165 166void EnumGenerator:: 167GenerateGetEnumDescriptorSpecializations(io::Printer* printer) { 168 printer->Print( 169 "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type " 170 "{};\n", 171 "classname", ClassName(descriptor_, true)); 172 if (HasDescriptorMethods(descriptor_->file(), options_)) { 173 printer->Print( 174 "template <>\n" 175 "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n" 176 " return $classname$_descriptor();\n" 177 "}\n", 178 "classname", ClassName(descriptor_, true)); 179 } 180} 181 182void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { 183 map<string, string> vars; 184 vars["nested_name"] = descriptor_->name(); 185 vars["classname"] = classname_; 186 vars["constexpr"] = options_.proto_h ? "constexpr " : ""; 187 printer->Print(vars, "typedef $classname$ $nested_name$;\n"); 188 189 for (int j = 0; j < descriptor_->value_count(); j++) { 190 vars["tag"] = EnumValueName(descriptor_->value(j)); 191 vars["deprecated_attr"] = descriptor_->value(j)->options().deprecated() ? 192 "PROTOBUF_DEPRECATED_ATTR " : ""; 193 printer->Print(vars, 194 "$deprecated_attr$static $constexpr$const $nested_name$ $tag$ =\n" 195 " $classname$_$tag$;\n"); 196 } 197 198 printer->Print(vars, 199 "static inline bool $nested_name$_IsValid(int value) {\n" 200 " return $classname$_IsValid(value);\n" 201 "}\n" 202 "static const $nested_name$ $nested_name$_MIN =\n" 203 " $classname$_$nested_name$_MIN;\n" 204 "static const $nested_name$ $nested_name$_MAX =\n" 205 " $classname$_$nested_name$_MAX;\n"); 206 if (generate_array_size_) { 207 printer->Print(vars, 208 "static const int $nested_name$_ARRAYSIZE =\n" 209 " $classname$_$nested_name$_ARRAYSIZE;\n"); 210 } 211 212 if (HasDescriptorMethods(descriptor_->file(), options_)) { 213 printer->Print(vars, 214 "static inline const ::google::protobuf::EnumDescriptor*\n" 215 "$nested_name$_descriptor() {\n" 216 " return $classname$_descriptor();\n" 217 "}\n"); 218 printer->Print(vars, 219 "static inline const ::std::string& " 220 "$nested_name$_Name($nested_name$ value) {" 221 "\n" 222 " return $classname$_Name(value);\n" 223 "}\n"); 224 printer->Print(vars, 225 "static inline bool $nested_name$_Parse(const ::std::string& name,\n" 226 " $nested_name$* value) {\n" 227 " return $classname$_Parse(name, value);\n" 228 "}\n"); 229 } 230} 231 232void EnumGenerator::GenerateDescriptorInitializer( 233 io::Printer* printer, int index) { 234 map<string, string> vars; 235 vars["classname"] = classname_; 236 vars["index"] = SimpleItoa(index); 237 238 if (descriptor_->containing_type() == NULL) { 239 printer->Print(vars, 240 "$classname$_descriptor_ = file->enum_type($index$);\n"); 241 } else { 242 vars["parent"] = ClassName(descriptor_->containing_type(), false); 243 printer->Print(vars, 244 "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n"); 245 } 246} 247 248void EnumGenerator::GenerateMethods(io::Printer* printer) { 249 map<string, string> vars; 250 vars["classname"] = classname_; 251 vars["constexpr"] = options_.proto_h ? "constexpr " : ""; 252 253 if (HasDescriptorMethods(descriptor_->file(), options_)) { 254 printer->Print(vars, 255 "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n" 256 " protobuf_AssignDescriptorsOnce();\n" 257 " return $classname$_descriptor_;\n" 258 "}\n"); 259 } 260 261 printer->Print(vars, 262 "bool $classname$_IsValid(int value) {\n" 263 " switch(value) {\n"); 264 265 // Multiple values may have the same number. Make sure we only cover 266 // each number once by first constructing a set containing all valid 267 // numbers, then printing a case statement for each element. 268 269 set<int> numbers; 270 for (int j = 0; j < descriptor_->value_count(); j++) { 271 const EnumValueDescriptor* value = descriptor_->value(j); 272 numbers.insert(value->number()); 273 } 274 275 for (set<int>::iterator iter = numbers.begin(); 276 iter != numbers.end(); ++iter) { 277 printer->Print( 278 " case $number$:\n", 279 "number", Int32ToString(*iter)); 280 } 281 282 printer->Print(vars, 283 " return true;\n" 284 " default:\n" 285 " return false;\n" 286 " }\n" 287 "}\n" 288 "\n"); 289 290 if (descriptor_->containing_type() != NULL) { 291 // We need to "define" the static constants which were declared in the 292 // header, to give the linker a place to put them. Or at least the C++ 293 // standard says we have to. MSVC actually insists that we do _not_ define 294 // them again in the .cc file, prior to VC++ 2015. 295 printer->Print("#if !defined(_MSC_VER) || _MSC_VER >= 1900\n"); 296 297 vars["parent"] = ClassName(descriptor_->containing_type(), false); 298 vars["nested_name"] = descriptor_->name(); 299 for (int i = 0; i < descriptor_->value_count(); i++) { 300 vars["value"] = EnumValueName(descriptor_->value(i)); 301 printer->Print(vars, 302 "$constexpr$const $classname$ $parent$::$value$;\n"); 303 } 304 printer->Print(vars, 305 "const $classname$ $parent$::$nested_name$_MIN;\n" 306 "const $classname$ $parent$::$nested_name$_MAX;\n"); 307 if (generate_array_size_) { 308 printer->Print(vars, 309 "const int $parent$::$nested_name$_ARRAYSIZE;\n"); 310 } 311 312 printer->Print("#endif // !defined(_MSC_VER) || _MSC_VER >= 1900\n"); 313 } 314} 315 316} // namespace cpp 317} // namespace compiler 318} // namespace protobuf 319} // namespace google 320