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 <set>
36 #include <map>
37
38 #include <google/protobuf/compiler/cpp/cpp_enum.h>
39 #include <google/protobuf/compiler/cpp/cpp_helpers.h>
40 #include <google/protobuf/io/printer.h>
41 #include <google/protobuf/stubs/strutil.h>
42
43 namespace google {
44 namespace protobuf {
45 namespace compiler {
46 namespace cpp {
47
48 namespace {
49 // The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
50 // is kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
51 // generation of the GOOGLE_ARRAYSIZE constant.
ShouldGenerateArraySize(const EnumDescriptor * descriptor)52 bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
53 int32 max_value = descriptor->value(0)->number();
54 for (int i = 0; i < descriptor->value_count(); i++) {
55 if (descriptor->value(i)->number() > max_value) {
56 max_value = descriptor->value(i)->number();
57 }
58 }
59 return max_value != kint32max;
60 }
61 } // namespace
62
EnumGenerator(const EnumDescriptor * descriptor,const Options & options)63 EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
64 const Options& options)
65 : descriptor_(descriptor),
66 classname_(ClassName(descriptor, false)),
67 options_(options),
68 generate_array_size_(ShouldGenerateArraySize(descriptor)) {
69 }
70
~EnumGenerator()71 EnumGenerator::~EnumGenerator() {}
72
GenerateDefinition(io::Printer * printer)73 void EnumGenerator::GenerateDefinition(io::Printer* printer) {
74 map<string, string> vars;
75 vars["classname"] = classname_;
76 vars["short_name"] = descriptor_->name();
77
78 printer->Print(vars, "enum $classname$ {\n");
79 printer->Indent();
80
81 const EnumValueDescriptor* min_value = descriptor_->value(0);
82 const EnumValueDescriptor* max_value = descriptor_->value(0);
83
84 for (int i = 0; i < descriptor_->value_count(); i++) {
85 vars["name"] = descriptor_->value(i)->name();
86 // In C++, an value of -2147483648 gets interpreted as the negative of
87 // 2147483648, and since 2147483648 can't fit in an integer, this produces a
88 // compiler warning. This works around that issue.
89 vars["number"] = Int32ToString(descriptor_->value(i)->number());
90 vars["prefix"] = (descriptor_->containing_type() == NULL) ?
91 "" : classname_ + "_";
92
93 if (i > 0) printer->Print(",\n");
94 printer->Print(vars, "$prefix$$name$ = $number$");
95
96 if (descriptor_->value(i)->number() < min_value->number()) {
97 min_value = descriptor_->value(i);
98 }
99 if (descriptor_->value(i)->number() > max_value->number()) {
100 max_value = descriptor_->value(i);
101 }
102 }
103
104 printer->Outdent();
105 printer->Print("\n};\n");
106
107 vars["min_name"] = min_value->name();
108 vars["max_name"] = max_value->name();
109
110 if (options_.dllexport_decl.empty()) {
111 vars["dllexport"] = "";
112 } else {
113 vars["dllexport"] = options_.dllexport_decl + " ";
114 }
115
116 printer->Print(vars,
117 "$dllexport$bool $classname$_IsValid(int value);\n"
118 "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n"
119 "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n");
120
121 if (generate_array_size_) {
122 printer->Print(vars,
123 "const int $prefix$$short_name$_ARRAYSIZE = "
124 "$prefix$$short_name$_MAX + 1;\n\n");
125 }
126
127 if (HasDescriptorMethods(descriptor_->file())) {
128 printer->Print(vars,
129 "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n");
130 // The _Name and _Parse methods
131 printer->Print(vars,
132 "inline const ::std::string& $classname$_Name($classname$ value) {\n"
133 " return ::google::protobuf::internal::NameOfEnum(\n"
134 " $classname$_descriptor(), value);\n"
135 "}\n");
136 printer->Print(vars,
137 "inline bool $classname$_Parse(\n"
138 " const ::std::string& name, $classname$* value) {\n"
139 " return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n"
140 " $classname$_descriptor(), name, value);\n"
141 "}\n");
142 }
143 }
144
145 void EnumGenerator::
GenerateGetEnumDescriptorSpecializations(io::Printer * printer)146 GenerateGetEnumDescriptorSpecializations(io::Printer* printer) {
147 if (HasDescriptorMethods(descriptor_->file())) {
148 printer->Print(
149 "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type {};\n"
150 "template <>\n"
151 "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n"
152 " return $classname$_descriptor();\n"
153 "}\n",
154 "classname", ClassName(descriptor_, true));
155 }
156 }
157
GenerateSymbolImports(io::Printer * printer)158 void EnumGenerator::GenerateSymbolImports(io::Printer* printer) {
159 map<string, string> vars;
160 vars["nested_name"] = descriptor_->name();
161 vars["classname"] = classname_;
162 printer->Print(vars, "typedef $classname$ $nested_name$;\n");
163
164 for (int j = 0; j < descriptor_->value_count(); j++) {
165 vars["tag"] = descriptor_->value(j)->name();
166 printer->Print(vars,
167 "static const $nested_name$ $tag$ = $classname$_$tag$;\n");
168 }
169
170 printer->Print(vars,
171 "static inline bool $nested_name$_IsValid(int value) {\n"
172 " return $classname$_IsValid(value);\n"
173 "}\n"
174 "static const $nested_name$ $nested_name$_MIN =\n"
175 " $classname$_$nested_name$_MIN;\n"
176 "static const $nested_name$ $nested_name$_MAX =\n"
177 " $classname$_$nested_name$_MAX;\n");
178 if (generate_array_size_) {
179 printer->Print(vars,
180 "static const int $nested_name$_ARRAYSIZE =\n"
181 " $classname$_$nested_name$_ARRAYSIZE;\n");
182 }
183
184 if (HasDescriptorMethods(descriptor_->file())) {
185 printer->Print(vars,
186 "static inline const ::google::protobuf::EnumDescriptor*\n"
187 "$nested_name$_descriptor() {\n"
188 " return $classname$_descriptor();\n"
189 "}\n");
190 printer->Print(vars,
191 "static inline const ::std::string& $nested_name$_Name($nested_name$ value) {\n"
192 " return $classname$_Name(value);\n"
193 "}\n");
194 printer->Print(vars,
195 "static inline bool $nested_name$_Parse(const ::std::string& name,\n"
196 " $nested_name$* value) {\n"
197 " return $classname$_Parse(name, value);\n"
198 "}\n");
199 }
200 }
201
GenerateDescriptorInitializer(io::Printer * printer,int index)202 void EnumGenerator::GenerateDescriptorInitializer(
203 io::Printer* printer, int index) {
204 map<string, string> vars;
205 vars["classname"] = classname_;
206 vars["index"] = SimpleItoa(index);
207
208 if (descriptor_->containing_type() == NULL) {
209 printer->Print(vars,
210 "$classname$_descriptor_ = file->enum_type($index$);\n");
211 } else {
212 vars["parent"] = ClassName(descriptor_->containing_type(), false);
213 printer->Print(vars,
214 "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n");
215 }
216 }
217
GenerateMethods(io::Printer * printer)218 void EnumGenerator::GenerateMethods(io::Printer* printer) {
219 map<string, string> vars;
220 vars["classname"] = classname_;
221
222 if (HasDescriptorMethods(descriptor_->file())) {
223 printer->Print(vars,
224 "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n"
225 " protobuf_AssignDescriptorsOnce();\n"
226 " return $classname$_descriptor_;\n"
227 "}\n");
228 }
229
230 printer->Print(vars,
231 "bool $classname$_IsValid(int value) {\n"
232 " switch(value) {\n");
233
234 // Multiple values may have the same number. Make sure we only cover
235 // each number once by first constructing a set containing all valid
236 // numbers, then printing a case statement for each element.
237
238 set<int> numbers;
239 for (int j = 0; j < descriptor_->value_count(); j++) {
240 const EnumValueDescriptor* value = descriptor_->value(j);
241 numbers.insert(value->number());
242 }
243
244 for (set<int>::iterator iter = numbers.begin();
245 iter != numbers.end(); ++iter) {
246 printer->Print(
247 " case $number$:\n",
248 "number", Int32ToString(*iter));
249 }
250
251 printer->Print(vars,
252 " return true;\n"
253 " default:\n"
254 " return false;\n"
255 " }\n"
256 "}\n"
257 "\n");
258
259 if (descriptor_->containing_type() != NULL) {
260 // We need to "define" the static constants which were declared in the
261 // header, to give the linker a place to put them. Or at least the C++
262 // standard says we have to. MSVC actually insists tha we do _not_ define
263 // them again in the .cc file.
264 printer->Print("#ifndef _MSC_VER\n");
265
266 vars["parent"] = ClassName(descriptor_->containing_type(), false);
267 vars["nested_name"] = descriptor_->name();
268 for (int i = 0; i < descriptor_->value_count(); i++) {
269 vars["value"] = descriptor_->value(i)->name();
270 printer->Print(vars,
271 "const $classname$ $parent$::$value$;\n");
272 }
273 printer->Print(vars,
274 "const $classname$ $parent$::$nested_name$_MIN;\n"
275 "const $classname$ $parent$::$nested_name$_MAX;\n");
276 if (generate_array_size_) {
277 printer->Print(vars,
278 "const int $parent$::$nested_name$_ARRAYSIZE;\n");
279 }
280
281 printer->Print("#endif // _MSC_VER\n");
282 }
283 }
284
285 } // namespace cpp
286 } // namespace compiler
287 } // namespace protobuf
288 } // namespace google
289