// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Author: kenton@google.com (Kenton Varda) // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. #include #include #include #include #include namespace google { namespace protobuf { namespace compiler { namespace cpp { namespace { // The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value // is ::google::protobuf::kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the // generation of the GOOGLE_ARRAYSIZE constant. bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) { int32 max_value = descriptor->value(0)->number(); for (int i = 0; i < descriptor->value_count(); i++) { if (descriptor->value(i)->number() > max_value) { max_value = descriptor->value(i)->number(); } } return max_value != ::google::protobuf::kint32max; } } // namespace EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, const Options& options) : descriptor_(descriptor), classname_(ClassName(descriptor, false)), options_(options), generate_array_size_(ShouldGenerateArraySize(descriptor)) { } EnumGenerator::~EnumGenerator() {} void EnumGenerator::FillForwardDeclaration( std::map* enum_names) { if (!options_.proto_h) { return; } (*enum_names)[classname_] = descriptor_; } void EnumGenerator::GenerateDefinition(io::Printer* printer) { std::map vars; vars["classname"] = classname_; vars["short_name"] = descriptor_->name(); vars["enumbase"] = options_.proto_h ? " : int" : ""; // These variables are placeholders to pick out the beginning and ends of // identifiers for annotations (when doing so with existing variables would // be ambiguous or impossible). They should never be set to anything but the // empty string. vars["{"] = ""; vars["}"] = ""; printer->Print(vars, "enum $classname$$enumbase$ {\n"); printer->Annotate("classname", descriptor_); printer->Indent(); const EnumValueDescriptor* min_value = descriptor_->value(0); const EnumValueDescriptor* max_value = descriptor_->value(0); for (int i = 0; i < descriptor_->value_count(); i++) { vars["name"] = EnumValueName(descriptor_->value(i)); // In C++, an value of -2147483648 gets interpreted as the negative of // 2147483648, and since 2147483648 can't fit in an integer, this produces a // compiler warning. This works around that issue. vars["number"] = Int32ToString(descriptor_->value(i)->number()); vars["prefix"] = (descriptor_->containing_type() == NULL) ? "" : classname_ + "_"; vars["deprecation"] = descriptor_->value(i)->options().deprecated() ? " PROTOBUF_DEPRECATED" : ""; if (i > 0) printer->Print(",\n"); printer->Print(vars, "${$$prefix$$name$$}$$deprecation$ = $number$"); printer->Annotate("{", "}", descriptor_->value(i)); if (descriptor_->value(i)->number() < min_value->number()) { min_value = descriptor_->value(i); } if (descriptor_->value(i)->number() > max_value->number()) { max_value = descriptor_->value(i); } } if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { // For new enum semantics: generate min and max sentinel values equal to // INT32_MIN and INT32_MAX if (descriptor_->value_count() > 0) printer->Print(",\n"); printer->Print(vars, "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min,\n" "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max"); } printer->Outdent(); printer->Print("\n};\n"); vars["min_name"] = EnumValueName(min_value); vars["max_name"] = EnumValueName(max_value); if (options_.dllexport_decl.empty()) { vars["dllexport"] = ""; } else { vars["dllexport"] = options_.dllexport_decl + " "; } printer->Print(vars, "$dllexport$bool $classname$_IsValid(int value);\n" "const $classname$ ${$$prefix$$short_name$_MIN$}$ = " "$prefix$$min_name$;\n"); printer->Annotate("{", "}", descriptor_); printer->Print(vars, "const $classname$ ${$$prefix$$short_name$_MAX$}$ = " "$prefix$$max_name$;\n"); printer->Annotate("{", "}", descriptor_); if (generate_array_size_) { printer->Print(vars, "const int ${$$prefix$$short_name$_ARRAYSIZE$}$ = " "$prefix$$short_name$_MAX + 1;\n\n"); printer->Annotate("{", "}", descriptor_); } if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(vars, "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n"); // The _Name and _Parse methods printer->Print( vars, "inline const ::std::string& $classname$_Name($classname$ value) {\n" " return ::google::protobuf::internal::NameOfEnum(\n" " $classname$_descriptor(), value);\n" "}\n"); printer->Print(vars, "inline bool $classname$_Parse(\n" " const ::std::string& name, $classname$* value) {\n" " return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n" " $classname$_descriptor(), name, value);\n" "}\n"); } } void EnumGenerator:: GenerateGetEnumDescriptorSpecializations(io::Printer* printer) { printer->Print( "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type " "{};\n", "classname", ClassName(descriptor_, true)); if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( "template <>\n" "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n" " return $classname$_descriptor();\n" "}\n", "classname", ClassName(descriptor_, true)); } } void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { std::map vars; vars["nested_name"] = descriptor_->name(); vars["classname"] = classname_; vars["constexpr"] = options_.proto_h ? "constexpr " : ""; vars["{"] = ""; vars["}"] = ""; printer->Print(vars, "typedef $classname$ $nested_name$;\n"); for (int j = 0; j < descriptor_->value_count(); j++) { vars["tag"] = EnumValueName(descriptor_->value(j)); vars["deprecated_attr"] = descriptor_->value(j)->options().deprecated() ? "GOOGLE_PROTOBUF_DEPRECATED_ATTR " : ""; printer->Print(vars, "$deprecated_attr$static $constexpr$const $nested_name$ ${$$tag$$}$ =\n" " $classname$_$tag$;\n"); printer->Annotate("{", "}", descriptor_->value(j)); } printer->Print(vars, "static inline bool $nested_name$_IsValid(int value) {\n" " return $classname$_IsValid(value);\n" "}\n" "static const $nested_name$ ${$$nested_name$_MIN$}$ =\n" " $classname$_$nested_name$_MIN;\n"); printer->Annotate("{", "}", descriptor_); printer->Print(vars, "static const $nested_name$ ${$$nested_name$_MAX$}$ =\n" " $classname$_$nested_name$_MAX;\n"); printer->Annotate("{", "}", descriptor_); if (generate_array_size_) { printer->Print(vars, "static const int ${$$nested_name$_ARRAYSIZE$}$ =\n" " $classname$_$nested_name$_ARRAYSIZE;\n"); printer->Annotate("{", "}", descriptor_); } if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(vars, "static inline const ::google::protobuf::EnumDescriptor*\n" "$nested_name$_descriptor() {\n" " return $classname$_descriptor();\n" "}\n"); printer->Print(vars, "static inline const ::std::string& " "$nested_name$_Name($nested_name$ value) {" "\n" " return $classname$_Name(value);\n" "}\n"); printer->Print(vars, "static inline bool $nested_name$_Parse(const ::std::string& name,\n" " $nested_name$* value) {\n" " return $classname$_Parse(name, value);\n" "}\n"); } } void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) { std::map vars; vars["classname"] = classname_; vars["index_in_metadata"] = SimpleItoa(idx); vars["constexpr"] = options_.proto_h ? "constexpr " : ""; vars["file_namespace"] = FileLevelNamespace(descriptor_->file()->name()); if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( vars, "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n" " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" " return " "$file_namespace$::file_level_enum_descriptors[$index_in_metadata$];\n" "}\n"); } printer->Print(vars, "bool $classname$_IsValid(int value) {\n" " switch (value) {\n"); // Multiple values may have the same number. Make sure we only cover // each number once by first constructing a set containing all valid // numbers, then printing a case statement for each element. std::set numbers; for (int j = 0; j < descriptor_->value_count(); j++) { const EnumValueDescriptor* value = descriptor_->value(j); numbers.insert(value->number()); } for (std::set::iterator iter = numbers.begin(); iter != numbers.end(); ++iter) { printer->Print( " case $number$:\n", "number", Int32ToString(*iter)); } printer->Print(vars, " return true;\n" " default:\n" " return false;\n" " }\n" "}\n" "\n"); if (descriptor_->containing_type() != NULL) { // We need to "define" the static constants which were declared in the // header, to give the linker a place to put them. Or at least the C++ // standard says we have to. MSVC actually insists that we do _not_ define // them again in the .cc file, prior to VC++ 2015. printer->Print("#if !defined(_MSC_VER) || _MSC_VER >= 1900\n"); vars["parent"] = ClassName(descriptor_->containing_type(), false); vars["nested_name"] = descriptor_->name(); for (int i = 0; i < descriptor_->value_count(); i++) { vars["value"] = EnumValueName(descriptor_->value(i)); printer->Print(vars, "$constexpr$const $classname$ $parent$::$value$;\n"); } printer->Print(vars, "const $classname$ $parent$::$nested_name$_MIN;\n" "const $classname$ $parent$::$nested_name$_MAX;\n"); if (generate_array_size_) { printer->Print(vars, "const int $parent$::$nested_name$_ARRAYSIZE;\n"); } printer->Print("#endif // !defined(_MSC_VER) || _MSC_VER >= 1900\n"); } } } // namespace cpp } // namespace compiler } // namespace protobuf } // namespace google