cpp_helpers.cc revision fbaaef999ba563838ebd00874ed8a1c01fbf286d
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
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 <vector>
36#include <google/protobuf/stubs/hash.h>
37
38#include <google/protobuf/compiler/cpp/cpp_helpers.h>
39#include <google/protobuf/stubs/common.h>
40#include <google/protobuf/stubs/strutil.h>
41#include <google/protobuf/stubs/substitute.h>
42
43namespace google {
44namespace protobuf {
45namespace compiler {
46namespace cpp {
47
48namespace {
49
50string DotsToUnderscores(const string& name) {
51  return StringReplace(name, ".", "_", true);
52}
53
54string DotsToColons(const string& name) {
55  return StringReplace(name, ".", "::", true);
56}
57
58const char* const kKeywordList[] = {
59  "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case",
60  "catch", "char", "class", "compl", "const", "const_cast", "continue",
61  "default", "delete", "do", "double", "dynamic_cast", "else", "enum",
62  "explicit", "extern", "false", "float", "for", "friend", "goto", "if",
63  "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq",
64  "operator", "or", "or_eq", "private", "protected", "public", "register",
65  "reinterpret_cast", "return", "short", "signed", "sizeof", "static",
66  "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
67  "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
68  "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
69};
70
71hash_set<string> MakeKeywordsMap() {
72  hash_set<string> result;
73  for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) {
74    result.insert(kKeywordList[i]);
75  }
76  return result;
77}
78
79hash_set<string> kKeywords = MakeKeywordsMap();
80
81string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
82  string result;
83  // Note:  I distrust ctype.h due to locales.
84  for (int i = 0; i < input.size(); i++) {
85    if ('a' <= input[i] && input[i] <= 'z') {
86      if (cap_next_letter) {
87        result += input[i] + ('A' - 'a');
88      } else {
89        result += input[i];
90      }
91      cap_next_letter = false;
92    } else if ('A' <= input[i] && input[i] <= 'Z') {
93      // Capital letters are left as-is.
94      result += input[i];
95      cap_next_letter = false;
96    } else if ('0' <= input[i] && input[i] <= '9') {
97      result += input[i];
98      cap_next_letter = true;
99    } else {
100      cap_next_letter = true;
101    }
102  }
103  return result;
104}
105
106}  // namespace
107
108const char kThickSeparator[] =
109  "// ===================================================================\n";
110const char kThinSeparator[] =
111  "// -------------------------------------------------------------------\n";
112
113string ClassName(const Descriptor* descriptor, bool qualified) {
114  // Find "outer", the descriptor of the top-level message in which
115  // "descriptor" is embedded.
116  const Descriptor* outer = descriptor;
117  while (outer->containing_type() != NULL) outer = outer->containing_type();
118
119  const string& outer_name = outer->full_name();
120  string inner_name = descriptor->full_name().substr(outer_name.size());
121
122  if (qualified) {
123    return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name);
124  } else {
125    return outer->name() + DotsToUnderscores(inner_name);
126  }
127}
128
129string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) {
130  if (enum_descriptor->containing_type() == NULL) {
131    if (qualified) {
132      return DotsToColons(enum_descriptor->full_name());
133    } else {
134      return enum_descriptor->name();
135    }
136  } else {
137    string result = ClassName(enum_descriptor->containing_type(), qualified);
138    result += '_';
139    result += enum_descriptor->name();
140    return result;
141  }
142}
143
144string FieldName(const FieldDescriptor* field) {
145  string result = field->name();
146  LowerString(&result);
147  if (kKeywords.count(result) > 0) {
148    result.append("_");
149  }
150  return result;
151}
152
153string FieldConstantName(const FieldDescriptor *field) {
154  string field_name = UnderscoresToCamelCase(field->name(), true);
155  string result = "k" + field_name + "FieldNumber";
156
157  if (!field->is_extension() &&
158      field->containing_type()->FindFieldByCamelcaseName(
159        field->camelcase_name()) != field) {
160    // This field's camelcase name is not unique.  As a hack, add the field
161    // number to the constant name.  This makes the constant rather useless,
162    // but what can we do?
163    result += "_" + SimpleItoa(field->number());
164  }
165
166  return result;
167}
168
169string StripProto(const string& filename) {
170  if (HasSuffixString(filename, ".protodevel")) {
171    return StripSuffixString(filename, ".protodevel");
172  } else {
173    return StripSuffixString(filename, ".proto");
174  }
175}
176
177const char* PrimitiveTypeName(FieldDescriptor::CppType type) {
178  switch (type) {
179    case FieldDescriptor::CPPTYPE_INT32  : return "::google::protobuf::int32";
180    case FieldDescriptor::CPPTYPE_INT64  : return "::google::protobuf::int64";
181    case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32";
182    case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64";
183    case FieldDescriptor::CPPTYPE_DOUBLE : return "double";
184    case FieldDescriptor::CPPTYPE_FLOAT  : return "float";
185    case FieldDescriptor::CPPTYPE_BOOL   : return "bool";
186    case FieldDescriptor::CPPTYPE_ENUM   : return "int";
187    case FieldDescriptor::CPPTYPE_STRING : return "::std::string";
188    case FieldDescriptor::CPPTYPE_MESSAGE: return NULL;
189
190    // No default because we want the compiler to complain if any new
191    // CppTypes are added.
192  }
193
194  GOOGLE_LOG(FATAL) << "Can't get here.";
195  return NULL;
196}
197
198const char* DeclaredTypeMethodName(FieldDescriptor::Type type) {
199  switch (type) {
200    case FieldDescriptor::TYPE_INT32   : return "Int32";
201    case FieldDescriptor::TYPE_INT64   : return "Int64";
202    case FieldDescriptor::TYPE_UINT32  : return "UInt32";
203    case FieldDescriptor::TYPE_UINT64  : return "UInt64";
204    case FieldDescriptor::TYPE_SINT32  : return "SInt32";
205    case FieldDescriptor::TYPE_SINT64  : return "SInt64";
206    case FieldDescriptor::TYPE_FIXED32 : return "Fixed32";
207    case FieldDescriptor::TYPE_FIXED64 : return "Fixed64";
208    case FieldDescriptor::TYPE_SFIXED32: return "SFixed32";
209    case FieldDescriptor::TYPE_SFIXED64: return "SFixed64";
210    case FieldDescriptor::TYPE_FLOAT   : return "Float";
211    case FieldDescriptor::TYPE_DOUBLE  : return "Double";
212
213    case FieldDescriptor::TYPE_BOOL    : return "Bool";
214    case FieldDescriptor::TYPE_ENUM    : return "Enum";
215
216    case FieldDescriptor::TYPE_STRING  : return "String";
217    case FieldDescriptor::TYPE_BYTES   : return "Bytes";
218    case FieldDescriptor::TYPE_GROUP   : return "Group";
219    case FieldDescriptor::TYPE_MESSAGE : return "Message";
220
221    // No default because we want the compiler to complain if any new
222    // types are added.
223  }
224  GOOGLE_LOG(FATAL) << "Can't get here.";
225  return "";
226}
227
228string DefaultValue(const FieldDescriptor* field) {
229  switch (field->cpp_type()) {
230    case FieldDescriptor::CPPTYPE_INT32:
231      return SimpleItoa(field->default_value_int32());
232    case FieldDescriptor::CPPTYPE_UINT32:
233      return SimpleItoa(field->default_value_uint32()) + "u";
234    case FieldDescriptor::CPPTYPE_INT64:
235      return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")";
236    case FieldDescriptor::CPPTYPE_UINT64:
237      return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")";
238    case FieldDescriptor::CPPTYPE_DOUBLE:
239      return SimpleDtoa(field->default_value_double());
240    case FieldDescriptor::CPPTYPE_FLOAT:
241      {
242        // If floating point value contains a period (.) or an exponent (either
243        // E or e), then append suffix 'f' to make it a floating-point literal.
244        string float_value = SimpleFtoa(field->default_value_float());
245        if (float_value.find_first_of(".eE") != string::npos) {
246          float_value.push_back('f');
247        }
248        return float_value;
249      }
250    case FieldDescriptor::CPPTYPE_BOOL:
251      return field->default_value_bool() ? "true" : "false";
252    case FieldDescriptor::CPPTYPE_ENUM:
253      // Lazy:  Generate a static_cast because we don't have a helper function
254      //   that constructs the full name of an enum value.
255      return strings::Substitute(
256          "static_cast< $0 >($1)",
257          ClassName(field->enum_type(), true),
258          field->default_value_enum()->number());
259    case FieldDescriptor::CPPTYPE_STRING:
260      return "\"" + CEscape(field->default_value_string()) + "\"";
261    case FieldDescriptor::CPPTYPE_MESSAGE:
262      return ClassName(field->message_type(), true) + "::default_instance()";
263  }
264  // Can't actually get here; make compiler happy.  (We could add a default
265  // case above but then we wouldn't get the nice compiler warning when a
266  // new type is added.)
267  GOOGLE_LOG(FATAL) << "Can't get here.";
268  return "";
269}
270
271// Convert a file name into a valid identifier.
272string FilenameIdentifier(const string& filename) {
273  string result;
274  for (int i = 0; i < filename.size(); i++) {
275    if (ascii_isalnum(filename[i])) {
276      result.push_back(filename[i]);
277    } else {
278      // Not alphanumeric.  To avoid any possibility of name conflicts we
279      // use the hex code for the character.
280      result.push_back('_');
281      char buffer[kFastToBufferSize];
282      result.append(FastHexToBuffer(static_cast<uint8>(filename[i]), buffer));
283    }
284  }
285  return result;
286}
287
288// Return the name of the AddDescriptors() function for a given file.
289string GlobalAddDescriptorsName(const string& filename) {
290  return "protobuf_AddDesc_" + FilenameIdentifier(filename);
291}
292
293// Return the name of the AssignDescriptors() function for a given file.
294string GlobalAssignDescriptorsName(const string& filename) {
295  return "protobuf_AssignDesc_" + FilenameIdentifier(filename);
296}
297
298// Return the name of the ShutdownFile() function for a given file.
299string GlobalShutdownFileName(const string& filename) {
300  return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
301}
302
303}  // namespace cpp
304}  // namespace compiler
305}  // namespace protobuf
306}  // namespace google
307