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
42 namespace google {
43 namespace protobuf {
44 namespace compiler {
45 namespace cpp {
46
47 namespace {
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.
ShouldGenerateArraySize(const EnumDescriptor * descriptor)51 bool 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
EnumGenerator(const EnumDescriptor * descriptor,const Options & options)62 EnumGenerator::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
~EnumGenerator()70 EnumGenerator::~EnumGenerator() {}
71
FillForwardDeclaration(map<string,const EnumDescriptor * > * enum_names)72 void EnumGenerator::FillForwardDeclaration(
73 map<string, const EnumDescriptor*>* enum_names) {
74 if (!options_.proto_h) {
75 return;
76 }
77 (*enum_names)[classname_] = descriptor_;
78 }
79
GenerateDefinition(io::Printer * printer)80 void 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
166 void EnumGenerator::
GenerateGetEnumDescriptorSpecializations(io::Printer * printer)167 GenerateGetEnumDescriptorSpecializations(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
GenerateSymbolImports(io::Printer * printer)182 void 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
GenerateDescriptorInitializer(io::Printer * printer,int index)232 void 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
GenerateMethods(io::Printer * printer)248 void 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