blob: 5ee6f0003baa868c08d05a5a5f9ab05f59c422c5 [file] [log] [blame]
temporal40ee5512008-07-10 02:12:20 +00001// Protocol Buffers - Google's data interchange format
kenton@google.com24bf56f2008-09-24 20:31:01 +00002// Copyright 2008 Google Inc. All rights reserved.
Feng Xiaoe4288622014-10-01 16:26:23 -07003// https://developers.google.com/protocol-buffers/
temporal40ee5512008-07-10 02:12:20 +00004//
kenton@google.com24bf56f2008-09-24 20:31:01 +00005// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
temporal40ee5512008-07-10 02:12:20 +00008//
kenton@google.com24bf56f2008-09-24 20:31:01 +00009// * 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.
temporal40ee5512008-07-10 02:12:20 +000018//
kenton@google.com24bf56f2008-09-24 20:31:01 +000019// 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.
temporal40ee5512008-07-10 02:12:20 +000030
31// Author: kenton@google.com (Kenton Varda)
32// Based on original Protocol Buffers design by
33// Sanjay Ghemawat, Jeff Dean, and others.
34
temporal40ee5512008-07-10 02:12:20 +000035#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
jieluo@google.com4de8f552014-07-18 00:47:59 +000047namespace {
48// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
Jisi Liu885b6122015-02-28 14:51:22 -080049// is ::google::protobuf::kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
jieluo@google.com4de8f552014-07-18 00:47:59 +000050// 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 }
Jisi Liu885b6122015-02-28 14:51:22 -080058 return max_value != ::google::protobuf::kint32max;
jieluo@google.com4de8f552014-07-18 00:47:59 +000059}
60} // namespace
61
temporal40ee5512008-07-10 02:12:20 +000062EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +000063 const Options& options)
temporal40ee5512008-07-10 02:12:20 +000064 : descriptor_(descriptor),
65 classname_(ClassName(descriptor, false)),
jieluo@google.com4de8f552014-07-18 00:47:59 +000066 options_(options),
67 generate_array_size_(ShouldGenerateArraySize(descriptor)) {
temporal40ee5512008-07-10 02:12:20 +000068}
69
70EnumGenerator::~EnumGenerator() {}
71
Feng Xiaoeee38b02015-08-22 18:25:48 -070072void EnumGenerator::FillForwardDeclaration(set<string>* enum_names) {
Bo Yang5db21732015-05-21 14:28:59 -070073 if (!options_.proto_h) {
74 return;
75 }
Feng Xiaoeee38b02015-08-22 18:25:48 -070076 enum_names->insert(classname_);
Bo Yang5db21732015-05-21 14:28:59 -070077}
78
temporal40ee5512008-07-10 02:12:20 +000079void EnumGenerator::GenerateDefinition(io::Printer* printer) {
80 map<string, string> vars;
81 vars["classname"] = classname_;
82 vars["short_name"] = descriptor_->name();
Bo Yang5db21732015-05-21 14:28:59 -070083 vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : "");
temporal40ee5512008-07-10 02:12:20 +000084
Bo Yang5db21732015-05-21 14:28:59 -070085 printer->Print(vars, "enum $enumbase$ {\n");
temporal40ee5512008-07-10 02:12:20 +000086 printer->Indent();
87
88 const EnumValueDescriptor* min_value = descriptor_->value(0);
89 const EnumValueDescriptor* max_value = descriptor_->value(0);
90
91 for (int i = 0; i < descriptor_->value_count(); i++) {
Chris Conroy0d77c822013-10-25 16:43:29 -040092 vars["name"] = EnumValueName(descriptor_->value(i));
jieluo@google.com4de8f552014-07-18 00:47:59 +000093 // In C++, an value of -2147483648 gets interpreted as the negative of
94 // 2147483648, and since 2147483648 can't fit in an integer, this produces a
95 // compiler warning. This works around that issue.
96 vars["number"] = Int32ToString(descriptor_->value(i)->number());
temporal40ee5512008-07-10 02:12:20 +000097 vars["prefix"] = (descriptor_->containing_type() == NULL) ?
98 "" : classname_ + "_";
99
kenton@google.com24bf56f2008-09-24 20:31:01 +0000100 if (i > 0) printer->Print(",\n");
101 printer->Print(vars, "$prefix$$name$ = $number$");
temporal40ee5512008-07-10 02:12:20 +0000102
103 if (descriptor_->value(i)->number() < min_value->number()) {
104 min_value = descriptor_->value(i);
105 }
106 if (descriptor_->value(i)->number() > max_value->number()) {
107 max_value = descriptor_->value(i);
108 }
109 }
110
Feng Xiao6ef984a2014-11-10 17:34:54 -0800111 if (HasPreservingUnknownEnumSemantics(descriptor_->file())) {
112 // For new enum semantics: generate min and max sentinel values equal to
113 // INT32_MIN and INT32_MAX
114 if (descriptor_->value_count() > 0) printer->Print(",\n");
115 printer->Print(vars,
Tres Seaver8b2aafe2014-11-17 19:35:04 -0500116 "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min,\n"
117 "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max");
Feng Xiao6ef984a2014-11-10 17:34:54 -0800118 }
119
temporal40ee5512008-07-10 02:12:20 +0000120 printer->Outdent();
kenton@google.com24bf56f2008-09-24 20:31:01 +0000121 printer->Print("\n};\n");
temporal40ee5512008-07-10 02:12:20 +0000122
Chris Conroy0d77c822013-10-25 16:43:29 -0400123 vars["min_name"] = EnumValueName(min_value);
124 vars["max_name"] = EnumValueName(max_value);
temporal40ee5512008-07-10 02:12:20 +0000125
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000126 if (options_.dllexport_decl.empty()) {
temporal40ee5512008-07-10 02:12:20 +0000127 vars["dllexport"] = "";
128 } else {
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000129 vars["dllexport"] = options_.dllexport_decl + " ";
temporal40ee5512008-07-10 02:12:20 +0000130 }
131
132 printer->Print(vars,
temporal40ee5512008-07-10 02:12:20 +0000133 "$dllexport$bool $classname$_IsValid(int value);\n"
134 "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n"
jieluo@google.com4de8f552014-07-18 00:47:59 +0000135 "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n");
136
137 if (generate_array_size_) {
138 printer->Print(vars,
139 "const int $prefix$$short_name$_ARRAYSIZE = "
140 "$prefix$$short_name$_MAX + 1;\n\n");
141 }
kenton@google.comd37d46d2009-04-25 02:53:47 +0000142
kenton@google.com80b1d622009-07-29 01:13:20 +0000143 if (HasDescriptorMethods(descriptor_->file())) {
144 printer->Print(vars,
145 "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n");
146 // The _Name and _Parse methods
147 printer->Print(vars,
148 "inline const ::std::string& $classname$_Name($classname$ value) {\n"
149 " return ::google::protobuf::internal::NameOfEnum(\n"
150 " $classname$_descriptor(), value);\n"
151 "}\n");
152 printer->Print(vars,
153 "inline bool $classname$_Parse(\n"
154 " const ::std::string& name, $classname$* value) {\n"
155 " return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n"
156 " $classname$_descriptor(), name, value);\n"
157 "}\n");
158 }
159}
160
161void EnumGenerator::
162GenerateGetEnumDescriptorSpecializations(io::Printer* printer) {
Jisi Liu885b6122015-02-28 14:51:22 -0800163 printer->Print(
164 "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type "
165 "{};\n",
166 "classname", ClassName(descriptor_, true));
kenton@google.com80b1d622009-07-29 01:13:20 +0000167 if (HasDescriptorMethods(descriptor_->file())) {
168 printer->Print(
169 "template <>\n"
170 "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n"
171 " return $classname$_descriptor();\n"
172 "}\n",
173 "classname", ClassName(descriptor_, true));
174 }
temporal40ee5512008-07-10 02:12:20 +0000175}
176
177void EnumGenerator::GenerateSymbolImports(io::Printer* printer) {
178 map<string, string> vars;
179 vars["nested_name"] = descriptor_->name();
180 vars["classname"] = classname_;
Clement Courbetb3d802d2016-02-19 08:24:29 +0100181 vars["constexpr"] = options_.proto_h ? "constexpr " : "";
temporal40ee5512008-07-10 02:12:20 +0000182 printer->Print(vars, "typedef $classname$ $nested_name$;\n");
183
184 for (int j = 0; j < descriptor_->value_count(); j++) {
Chris Conroy0d77c822013-10-25 16:43:29 -0400185 vars["tag"] = EnumValueName(descriptor_->value(j));
temporal40ee5512008-07-10 02:12:20 +0000186 printer->Print(vars,
Clement Courbetb3d802d2016-02-19 08:24:29 +0100187 "static $constexpr$const $nested_name$ $tag$ = $classname$_$tag$;\n");
temporal40ee5512008-07-10 02:12:20 +0000188 }
189
190 printer->Print(vars,
temporal40ee5512008-07-10 02:12:20 +0000191 "static inline bool $nested_name$_IsValid(int value) {\n"
192 " return $classname$_IsValid(value);\n"
193 "}\n"
194 "static const $nested_name$ $nested_name$_MIN =\n"
195 " $classname$_$nested_name$_MIN;\n"
196 "static const $nested_name$ $nested_name$_MAX =\n"
jieluo@google.com4de8f552014-07-18 00:47:59 +0000197 " $classname$_$nested_name$_MAX;\n");
198 if (generate_array_size_) {
199 printer->Print(vars,
200 "static const int $nested_name$_ARRAYSIZE =\n"
201 " $classname$_$nested_name$_ARRAYSIZE;\n");
202 }
kenton@google.com80b1d622009-07-29 01:13:20 +0000203
204 if (HasDescriptorMethods(descriptor_->file())) {
205 printer->Print(vars,
206 "static inline const ::google::protobuf::EnumDescriptor*\n"
207 "$nested_name$_descriptor() {\n"
208 " return $classname$_descriptor();\n"
kenton@google.comfccb1462009-12-18 02:11:36 +0000209 "}\n");
210 printer->Print(vars,
kenton@google.com80b1d622009-07-29 01:13:20 +0000211 "static inline const ::std::string& $nested_name$_Name($nested_name$ value) {\n"
212 " return $classname$_Name(value);\n"
kenton@google.comfccb1462009-12-18 02:11:36 +0000213 "}\n");
214 printer->Print(vars,
kenton@google.com80b1d622009-07-29 01:13:20 +0000215 "static inline bool $nested_name$_Parse(const ::std::string& name,\n"
216 " $nested_name$* value) {\n"
217 " return $classname$_Parse(name, value);\n"
218 "}\n");
219 }
temporal40ee5512008-07-10 02:12:20 +0000220}
221
222void EnumGenerator::GenerateDescriptorInitializer(
223 io::Printer* printer, int index) {
224 map<string, string> vars;
225 vars["classname"] = classname_;
226 vars["index"] = SimpleItoa(index);
227
228 if (descriptor_->containing_type() == NULL) {
229 printer->Print(vars,
230 "$classname$_descriptor_ = file->enum_type($index$);\n");
231 } else {
232 vars["parent"] = ClassName(descriptor_->containing_type(), false);
233 printer->Print(vars,
234 "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n");
235 }
236}
237
238void EnumGenerator::GenerateMethods(io::Printer* printer) {
239 map<string, string> vars;
240 vars["classname"] = classname_;
Clement Courbetb3d802d2016-02-19 08:24:29 +0100241 vars["constexpr"] = options_.proto_h ? "constexpr " : "";
temporal40ee5512008-07-10 02:12:20 +0000242
kenton@google.com80b1d622009-07-29 01:13:20 +0000243 if (HasDescriptorMethods(descriptor_->file())) {
244 printer->Print(vars,
245 "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n"
246 " protobuf_AssignDescriptorsOnce();\n"
247 " return $classname$_descriptor_;\n"
248 "}\n");
249 }
250
temporal40ee5512008-07-10 02:12:20 +0000251 printer->Print(vars,
temporal40ee5512008-07-10 02:12:20 +0000252 "bool $classname$_IsValid(int value) {\n"
253 " switch(value) {\n");
254
255 // Multiple values may have the same number. Make sure we only cover
256 // each number once by first constructing a set containing all valid
257 // numbers, then printing a case statement for each element.
258
259 set<int> numbers;
260 for (int j = 0; j < descriptor_->value_count(); j++) {
261 const EnumValueDescriptor* value = descriptor_->value(j);
262 numbers.insert(value->number());
263 }
264
265 for (set<int>::iterator iter = numbers.begin();
266 iter != numbers.end(); ++iter) {
267 printer->Print(
268 " case $number$:\n",
jieluo@google.com4de8f552014-07-18 00:47:59 +0000269 "number", Int32ToString(*iter));
temporal40ee5512008-07-10 02:12:20 +0000270 }
271
272 printer->Print(vars,
273 " return true;\n"
274 " default:\n"
275 " return false;\n"
276 " }\n"
277 "}\n"
278 "\n");
279
280 if (descriptor_->containing_type() != NULL) {
281 // We need to "define" the static constants which were declared in the
282 // header, to give the linker a place to put them. Or at least the C++
Bruce Dawson86ba70e2015-10-29 12:41:29 -0700283 // standard says we have to. MSVC actually insists that we do _not_ define
284 // them again in the .cc file, prior to VC++ 2015.
285 printer->Print("#if !defined(_MSC_VER) || _MSC_VER >= 1900\n");
temporal40ee5512008-07-10 02:12:20 +0000286
287 vars["parent"] = ClassName(descriptor_->containing_type(), false);
288 vars["nested_name"] = descriptor_->name();
289 for (int i = 0; i < descriptor_->value_count(); i++) {
Chris Conroy0d77c822013-10-25 16:43:29 -0400290 vars["value"] = EnumValueName(descriptor_->value(i));
temporal40ee5512008-07-10 02:12:20 +0000291 printer->Print(vars,
Clement Courbetb3d802d2016-02-19 08:24:29 +0100292 "$constexpr$const $classname$ $parent$::$value$;\n");
temporal40ee5512008-07-10 02:12:20 +0000293 }
294 printer->Print(vars,
295 "const $classname$ $parent$::$nested_name$_MIN;\n"
jieluo@google.com4de8f552014-07-18 00:47:59 +0000296 "const $classname$ $parent$::$nested_name$_MAX;\n");
297 if (generate_array_size_) {
298 printer->Print(vars,
299 "const int $parent$::$nested_name$_ARRAYSIZE;\n");
300 }
temporal40ee5512008-07-10 02:12:20 +0000301
Bruce Dawson86ba70e2015-10-29 12:41:29 -0700302 printer->Print("#endif // !defined(_MSC_VER) || _MSC_VER >= 1900\n");
temporal40ee5512008-07-10 02:12:20 +0000303 }
304}
305
306} // namespace cpp
307} // namespace compiler
308} // namespace protobuf
309} // namespace google