|
@@ -57,6 +57,17 @@ bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
|
|
}
|
|
}
|
|
return max_value != kint32max;
|
|
return max_value != kint32max;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+// Returns the number of unique numeric enum values. This is less than
|
|
|
|
+// descriptor->value_count() when there are aliased values.
|
|
|
|
+int CountUniqueValues(const EnumDescriptor* descriptor) {
|
|
|
|
+ std::set<int> values;
|
|
|
|
+ for (int i = 0; i < descriptor->value_count(); ++i) {
|
|
|
|
+ values.insert(descriptor->value(i)->number());
|
|
|
|
+ }
|
|
|
|
+ return values.size();
|
|
|
|
+}
|
|
|
|
+
|
|
} // namespace
|
|
} // namespace
|
|
|
|
|
|
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
|
|
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
|
|
@@ -143,25 +154,42 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) {
|
|
format(
|
|
format(
|
|
"$dllexport_decl $const ::$proto_ns$::EnumDescriptor* "
|
|
"$dllexport_decl $const ::$proto_ns$::EnumDescriptor* "
|
|
"$classname$_descriptor();\n");
|
|
"$classname$_descriptor();\n");
|
|
- // The _Name and _Parse methods
|
|
|
|
- // Support a stricter, type-checked enum-to-string method that
|
|
|
|
- // statically checks whether the parameter is the exact enum type or is
|
|
|
|
- // an integral type.
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // The _Name and _Parse functions. The lite implementation is table-based, so
|
|
|
|
+ // we make sure to keep the tables hidden in the .cc file.
|
|
|
|
+ if (!HasDescriptorMethods(descriptor_->file(), options_)) {
|
|
|
|
+ format("const std::string& $classname$_Name($classname$ value);\n");
|
|
|
|
+ }
|
|
|
|
+ // The _Name() function accepts the enum type itself but also any integral
|
|
|
|
+ // type.
|
|
|
|
+ format(
|
|
|
|
+ "template<typename T>\n"
|
|
|
|
+ "inline const std::string& $classname$_Name(T enum_t_value) {\n"
|
|
|
|
+ " static_assert(::std::is_same<T, $classname$>::value ||\n"
|
|
|
|
+ " ::std::is_integral<T>::value,\n"
|
|
|
|
+ " \"Incorrect type passed to function $classname$_Name.\");\n");
|
|
|
|
+ if (HasDescriptorMethods(descriptor_->file(), options_)) {
|
|
format(
|
|
format(
|
|
- "template<typename T>\n"
|
|
|
|
- "inline const std::string& $classname$_Name(T enum_t_value) {\n"
|
|
|
|
- " static_assert(::std::is_same<T, $classname$>::value ||\n"
|
|
|
|
- " ::std::is_integral<T>::value,\n"
|
|
|
|
- " \"Incorrect type passed to function $classname$_Name.\");\n"
|
|
|
|
" return ::$proto_ns$::internal::NameOfEnum(\n"
|
|
" return ::$proto_ns$::internal::NameOfEnum(\n"
|
|
- " $classname$_descriptor(), enum_t_value);\n"
|
|
|
|
- "}\n");
|
|
|
|
|
|
+ " $classname$_descriptor(), enum_t_value);\n");
|
|
|
|
+ } else {
|
|
|
|
+ format(
|
|
|
|
+ " return $classname$_Name(static_cast<$classname$>(enum_t_value));\n");
|
|
|
|
+ }
|
|
|
|
+ format("}\n");
|
|
|
|
+
|
|
|
|
+ if (HasDescriptorMethods(descriptor_->file(), options_)) {
|
|
format(
|
|
format(
|
|
"inline bool $classname$_Parse(\n"
|
|
"inline bool $classname$_Parse(\n"
|
|
" const std::string& name, $classname$* value) {\n"
|
|
" const std::string& name, $classname$* value) {\n"
|
|
" return ::$proto_ns$::internal::ParseNamedEnum<$classname$>(\n"
|
|
" return ::$proto_ns$::internal::ParseNamedEnum<$classname$>(\n"
|
|
" $classname$_descriptor(), name, value);\n"
|
|
" $classname$_descriptor(), name, value);\n"
|
|
"}\n");
|
|
"}\n");
|
|
|
|
+ } else {
|
|
|
|
+ format(
|
|
|
|
+ "bool $classname$_Parse(\n"
|
|
|
|
+ " const std::string& name, $classname$* value);\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -216,24 +244,21 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) const {
|
|
"$nested_name$_descriptor() {\n"
|
|
"$nested_name$_descriptor() {\n"
|
|
" return $classname$_descriptor();\n"
|
|
" return $classname$_descriptor();\n"
|
|
"}\n");
|
|
"}\n");
|
|
- // Support a stricter, type-checked enum-to-string method that
|
|
|
|
- // statically checks whether the parameter is the exact enum type or is
|
|
|
|
- // an integral type.
|
|
|
|
- format(
|
|
|
|
- "template<typename T>\n"
|
|
|
|
- "static inline const std::string& $nested_name$_Name(T enum_t_value) "
|
|
|
|
- "{\n"
|
|
|
|
- " static_assert(::std::is_same<T, $resolved_name$>::value ||\n"
|
|
|
|
- " ::std::is_integral<T>::value,\n"
|
|
|
|
- " \"Incorrect type passed to function $nested_name$_Name.\");\n"
|
|
|
|
- " return $classname$_Name(enum_t_value);\n"
|
|
|
|
- "}\n");
|
|
|
|
- format(
|
|
|
|
- "static inline bool $nested_name$_Parse(const std::string& name,\n"
|
|
|
|
- " $resolved_name$* value) {\n"
|
|
|
|
- " return $classname$_Parse(name, value);\n"
|
|
|
|
- "}\n");
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ format(
|
|
|
|
+ "template<typename T>\n"
|
|
|
|
+ "static inline const std::string& $nested_name$_Name(T enum_t_value) {\n"
|
|
|
|
+ " static_assert(::std::is_same<T, $resolved_name$>::value ||\n"
|
|
|
|
+ " ::std::is_integral<T>::value,\n"
|
|
|
|
+ " \"Incorrect type passed to function $nested_name$_Name.\");\n"
|
|
|
|
+ " return $classname$_Name(enum_t_value);\n"
|
|
|
|
+ "}\n");
|
|
|
|
+ format(
|
|
|
|
+ "static inline bool $nested_name$_Parse(const std::string& name,\n"
|
|
|
|
+ " $resolved_name$* value) {\n"
|
|
|
|
+ " return $classname$_Parse(name, value);\n"
|
|
|
|
+ "}\n");
|
|
}
|
|
}
|
|
|
|
|
|
void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) {
|
|
void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) {
|
|
@@ -274,6 +299,104 @@ void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) {
|
|
"}\n"
|
|
"}\n"
|
|
"\n");
|
|
"\n");
|
|
|
|
|
|
|
|
+ if (!HasDescriptorMethods(descriptor_->file(), options_)) {
|
|
|
|
+ // In lite mode (where descriptors are unavailable), we generate separate
|
|
|
|
+ // tables for mapping between enum names and numbers. The _entries table
|
|
|
|
+ // contains the bulk of the data and is sorted by name, while
|
|
|
|
+ // _entries_by_number is sorted by number and just contains pointers into
|
|
|
|
+ // _entries. The two tables allow mapping from name to number and number to
|
|
|
|
+ // name, both in time logarithmic in the number of enum entries. This could
|
|
|
|
+ // probably be made faster, but for now the tables are intended to be simple
|
|
|
|
+ // and compact.
|
|
|
|
+ //
|
|
|
|
+ // Enums with allow_alias = true support multiple entries with the same
|
|
|
|
+ // numerical value. In cases where there are multiple names for the same
|
|
|
|
+ // number, we treat the first name appearing in the .proto file as the
|
|
|
|
+ // canonical one.
|
|
|
|
+ std::map<std::string, int> name_to_number;
|
|
|
|
+ std::map<int, std::string> number_to_canonical_name;
|
|
|
|
+ for (int i = 0; i < descriptor_->value_count(); i++) {
|
|
|
|
+ const EnumValueDescriptor* value = descriptor_->value(i);
|
|
|
|
+ name_to_number.emplace(value->name(), value->number());
|
|
|
|
+ // The same number may appear with multiple names, so we use emplace() to
|
|
|
|
+ // let the first name win.
|
|
|
|
+ number_to_canonical_name.emplace(value->number(), value->name());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ format(
|
|
|
|
+ "static ::$proto_ns$::internal::ExplicitlyConstructed<std::string> "
|
|
|
|
+ "$classname$_strings[$1$] = {};\n\n",
|
|
|
|
+ CountUniqueValues(descriptor_));
|
|
|
|
+
|
|
|
|
+ // We concatenate all the names for a given enum into one big string
|
|
|
|
+ // literal. If instead we store an array of string literals, the linker
|
|
|
|
+ // seems to put all enum strings for a given .proto file in the same
|
|
|
|
+ // section, which hinders its ability to strip out unused strings.
|
|
|
|
+ format("static const char $classname$_names[] =");
|
|
|
|
+ for (const auto& p : name_to_number) {
|
|
|
|
+ format("\n \"$1$\"", p.first);
|
|
|
|
+ }
|
|
|
|
+ format(";\n\n");
|
|
|
|
+
|
|
|
|
+ format(
|
|
|
|
+ "static const ::$proto_ns$::internal::EnumEntry $classname$_entries[] "
|
|
|
|
+ "= {\n");
|
|
|
|
+ int i = 0;
|
|
|
|
+ std::map<int, int> number_to_index;
|
|
|
|
+ int data_index = 0;
|
|
|
|
+ for (const auto& p : name_to_number) {
|
|
|
|
+ format(" { {$classname$_names + $1$, $2$}, $3$ },\n", data_index,
|
|
|
|
+ p.first.size(), p.second);
|
|
|
|
+ if (number_to_canonical_name[p.second] == p.first) {
|
|
|
|
+ number_to_index.emplace(p.second, i);
|
|
|
|
+ }
|
|
|
|
+ ++i;
|
|
|
|
+ data_index += p.first.size();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ format(
|
|
|
|
+ "};\n"
|
|
|
|
+ "\n"
|
|
|
|
+ "static const int $classname$_entries_by_number[] = {\n");
|
|
|
|
+ for (const auto& p : number_to_index) {
|
|
|
|
+ format(" $1$, // $2$ -> $3$\n", p.second, p.first,
|
|
|
|
+ number_to_canonical_name[p.first]);
|
|
|
|
+ }
|
|
|
|
+ format(
|
|
|
|
+ "};\n"
|
|
|
|
+ "\n");
|
|
|
|
+
|
|
|
|
+ format(
|
|
|
|
+ "const std::string& $classname$_Name(\n"
|
|
|
|
+ " $classname$ value) {\n"
|
|
|
|
+ " static const bool dummy =\n"
|
|
|
|
+ " ::$proto_ns$::internal::InitializeEnumStrings(\n"
|
|
|
|
+ " $classname$_entries,\n"
|
|
|
|
+ " $classname$_entries_by_number,\n"
|
|
|
|
+ " $1$, $classname$_strings);\n"
|
|
|
|
+ " (void) dummy;\n"
|
|
|
|
+ " int idx = ::$proto_ns$::internal::LookUpEnumName(\n"
|
|
|
|
+ " $classname$_entries,\n"
|
|
|
|
+ " $classname$_entries_by_number,\n"
|
|
|
|
+ " $1$, value);\n"
|
|
|
|
+ " return idx == -1 ? ::$proto_ns$::internal::GetEmptyString() :\n"
|
|
|
|
+ " $classname$_strings[idx].get();\n"
|
|
|
|
+ "}\n",
|
|
|
|
+ CountUniqueValues(descriptor_));
|
|
|
|
+ format(
|
|
|
|
+ "bool $classname$_Parse(\n"
|
|
|
|
+ " const std::string& name, $classname$* value) {\n"
|
|
|
|
+ " int int_value;\n"
|
|
|
|
+ " bool success = ::$proto_ns$::internal::LookUpEnumValue(\n"
|
|
|
|
+ " $classname$_entries, $1$, name, &int_value);\n"
|
|
|
|
+ " if (success) {\n"
|
|
|
|
+ " *value = static_cast<$classname$>(int_value);\n"
|
|
|
|
+ " }\n"
|
|
|
|
+ " return success;\n"
|
|
|
|
+ "}\n",
|
|
|
|
+ descriptor_->value_count());
|
|
|
|
+ }
|
|
|
|
+
|
|
if (descriptor_->containing_type() != NULL) {
|
|
if (descriptor_->containing_type() != NULL) {
|
|
std::string parent = ClassName(descriptor_->containing_type(), false);
|
|
std::string parent = ClassName(descriptor_->containing_type(), false);
|
|
// Before C++17, we must define the static constants which were
|
|
// Before C++17, we must define the static constants which were
|