$search
00001 #!/usr/bin/env python 00002 # Software License Agreement (BSD License) 00003 # 00004 # Copyright (c) 2009, Willow Garage, Inc. 00005 # All rights reserved. 00006 # 00007 # Redistribution and use in source and binary forms, with or without 00008 # modification, are permitted provided that the following conditions 00009 # are met: 00010 # 00011 # * Redistributions of source code must retain the above copyright 00012 # notice, this list of conditions and the following disclaimer. 00013 # * Redistributions in binary form must reproduce the above 00014 # copyright notice, this list of conditions and the following 00015 # disclaimer in the documentation and/or other materials provided 00016 # with the distribution. 00017 # * Neither the name of Willow Garage, Inc. nor the names of its 00018 # contributors may be used to endorse or promote products derived 00019 # from this software without specific prior written permission. 00020 # 00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 # POSSIBILITY OF SUCH DAMAGE. 00033 # 00034 00035 ## ROS message source code generation for C++ 00036 ## 00037 ## Converts ROS .msg files in a package into C++ source code implementations. 00038 00039 import roslib; roslib.load_manifest('roscpp') 00040 00041 import sys 00042 import os 00043 import traceback 00044 00045 import roslib.msgs 00046 import roslib.packages 00047 import roslib.gentools 00048 00049 try: 00050 from cStringIO import StringIO #Python 2.x 00051 except ImportError: 00052 from io import StringIO #Python 3.x 00053 00054 MSG_TYPE_TO_CPP = {'byte': 'int8_t', 'char': 'uint8_t', 00055 'bool': 'uint8_t', 00056 'uint8': 'uint8_t', 'int8': 'int8_t', 00057 'uint16': 'uint16_t', 'int16': 'int16_t', 00058 'uint32': 'uint32_t', 'int32': 'int32_t', 00059 'uint64': 'uint64_t', 'int64': 'int64_t', 00060 'float32': 'float', 00061 'float64': 'double', 00062 'string': 'std::basic_string<char, std::char_traits<char>, typename ContainerAllocator::template rebind<char>::other > ', 00063 'time': 'ros::Time', 00064 'duration': 'ros::Duration'} 00065 00066 def msg_type_to_cpp(type): 00067 """ 00068 Converts a message type (e.g. uint32, std_msgs/String, etc.) into the C++ declaration 00069 for that type (e.g. uint32_t, std_msgs::String_<ContainerAllocator>) 00070 00071 @param type: The message type 00072 @type type: str 00073 @return: The C++ declaration 00074 @rtype: str 00075 """ 00076 (base_type, is_array, array_len) = roslib.msgs.parse_type(type) 00077 cpp_type = None 00078 if (roslib.msgs.is_builtin(base_type)): 00079 cpp_type = MSG_TYPE_TO_CPP[base_type] 00080 elif (len(base_type.split('/')) == 1): 00081 if (roslib.msgs.is_header_type(base_type)): 00082 cpp_type = ' ::std_msgs::Header_<ContainerAllocator> ' 00083 else: 00084 cpp_type = '%s_<ContainerAllocator> '%(base_type) 00085 else: 00086 pkg = base_type.split('/')[0] 00087 msg = base_type.split('/')[1] 00088 cpp_type = ' ::%s::%s_<ContainerAllocator> '%(pkg, msg) 00089 00090 if (is_array): 00091 if (array_len is None): 00092 return 'std::vector<%s, typename ContainerAllocator::template rebind<%s>::other > '%(cpp_type, cpp_type) 00093 else: 00094 return 'boost::array<%s, %s> '%(cpp_type, array_len) 00095 else: 00096 return cpp_type 00097 00098 def cpp_message_declarations(name_prefix, msg): 00099 """ 00100 Returns the different possible C++ declarations for a message given the message itself. 00101 00102 @param name_prefix: The C++ prefix to be prepended to the name, e.g. "std_msgs::" 00103 @type name_prefix: str 00104 @param msg: The message type 00105 @type msg: str 00106 @return: A tuple of 3 different names. cpp_message_decelarations("std_msgs::", "String") returns the tuple 00107 ("std_msgs::String_", "std_msgs::String_<ContainerAllocator>", "std_msgs::String") 00108 @rtype: str 00109 """ 00110 pkg, basetype = roslib.names.package_resource_name(msg) 00111 cpp_name = ' ::%s%s'%(name_prefix, msg) 00112 if (pkg): 00113 cpp_name = ' ::%s::%s'%(pkg, basetype) 00114 return ('%s_'%(cpp_name), '%s_<ContainerAllocator> '%(cpp_name), '%s'%(cpp_name)) 00115 00116 def write_begin(s, spec, file): 00117 """ 00118 Writes the beginning of the header file: a comment saying it's auto-generated and the include guards 00119 00120 @param s: The stream to write to 00121 @type s: stream 00122 @param spec: The spec 00123 @type spec: roslib.msgs.MsgSpec 00124 @param file: The file this message is being generated for 00125 @type file: str 00126 """ 00127 s.write("/* Auto-generated by genmsg_cpp for file %s */\n"%(file)) 00128 s.write('#ifndef %s_MESSAGE_%s_H\n'%(spec.package.upper(), spec.short_name.upper())) 00129 s.write('#define %s_MESSAGE_%s_H\n'%(spec.package.upper(), spec.short_name.upper())) 00130 00131 def write_end(s, spec): 00132 """ 00133 Writes the end of the header file: the ending of the include guards 00134 00135 @param s: The stream to write to 00136 @type s: stream 00137 @param spec: The spec 00138 @type spec: roslib.msgs.MsgSpec 00139 """ 00140 s.write('#endif // %s_MESSAGE_%s_H\n'%(spec.package.upper(), spec.short_name.upper())) 00141 00142 def write_generic_includes(s): 00143 """ 00144 Writes the includes that all messages need 00145 00146 @param s: The stream to write to 00147 @type s: stream 00148 """ 00149 s.write('#include <string>\n') 00150 s.write('#include <vector>\n') 00151 s.write('#include <map>\n') 00152 s.write('#include <ostream>\n') 00153 s.write('#include "ros/serialization.h"\n') 00154 s.write('#include "ros/builtin_message_traits.h"\n') 00155 s.write('#include "ros/message_operations.h"\n') 00156 s.write('#include "ros/time.h"\n\n') 00157 s.write('#include "ros/macros.h"\n\n') 00158 s.write('#include "ros/assert.h"\n\n') 00159 00160 def write_includes(s, spec): 00161 """ 00162 Writes the message-specific includes 00163 00164 @param s: The stream to write to 00165 @type s: stream 00166 @param spec: The message spec to iterate over 00167 @type spec: roslib.msgs.MsgSpec 00168 """ 00169 for field in spec.parsed_fields(): 00170 if (not field.is_builtin): 00171 if (field.is_header): 00172 s.write('#include "std_msgs/Header.h"\n') 00173 else: 00174 (pkg, name) = roslib.names.package_resource_name(field.base_type) 00175 pkg = pkg or spec.package # convert '' to package 00176 s.write('#include "%s/%s.h"\n'%(pkg, name)) 00177 00178 s.write('\n') 00179 00180 00181 def write_struct(s, spec, cpp_name_prefix, extra_deprecated_traits = {}): 00182 """ 00183 Writes the entire message struct: declaration, constructors, members, constants and (deprecated) member functions 00184 @param s: The stream to write to 00185 @type s: stream 00186 @param spec: The message spec 00187 @type spec: roslib.msgs.MsgSpec 00188 @param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::" 00189 @type cpp_name_prefix: str 00190 """ 00191 00192 msg = spec.short_name 00193 s.write('template <class ContainerAllocator>\n') 00194 s.write('struct %s_ {\n'%(msg)) 00195 s.write(' typedef %s_<ContainerAllocator> Type;\n\n'%(msg)) 00196 00197 write_constructors(s, spec, cpp_name_prefix) 00198 write_members(s, spec) 00199 write_constant_declarations(s, spec) 00200 00201 gendeps_dict = roslib.gentools.get_dependencies(spec, spec.package, compute_files=False) 00202 md5sum = roslib.gentools.compute_md5(gendeps_dict) 00203 full_text = compute_full_text_escaped(gendeps_dict) 00204 00205 write_deprecated_member_functions(s, spec, dict(list({'MD5Sum': md5sum, 'DataType': '%s/%s'%(spec.package, spec.short_name), 'MessageDefinition': full_text}.items()) + list(extra_deprecated_traits.items()))) 00206 00207 (cpp_msg_unqualified, cpp_msg_with_alloc, cpp_msg_base) = cpp_message_declarations(cpp_name_prefix, msg) 00208 s.write(' typedef boost::shared_ptr<%s> Ptr;\n'%(cpp_msg_with_alloc)) 00209 s.write(' typedef boost::shared_ptr<%s const> ConstPtr;\n'%(cpp_msg_with_alloc)) 00210 s.write(' boost::shared_ptr<std::map<std::string, std::string> > __connection_header;\n') 00211 00212 s.write('}; // struct %s\n'%(msg)) 00213 00214 s.write('typedef %s_<std::allocator<void> > %s;\n\n'%(cpp_msg_base, msg)) 00215 s.write('typedef boost::shared_ptr<%s> %sPtr;\n'%(cpp_msg_base, msg)) 00216 s.write('typedef boost::shared_ptr<%s const> %sConstPtr;\n\n'%(cpp_msg_base, msg)) 00217 00218 def default_value(type): 00219 """ 00220 Returns the value to initialize a message member with. 0 for integer types, 0.0 for floating point, false for bool, 00221 empty string for everything else 00222 00223 @param type: The type 00224 @type type: str 00225 """ 00226 if type in ['byte', 'int8', 'int16', 'int32', 'int64', 00227 'char', 'uint8', 'uint16', 'uint32', 'uint64']: 00228 return '0' 00229 elif type in ['float32', 'float64']: 00230 return '0.0' 00231 elif type == 'bool': 00232 return 'false' 00233 00234 return "" 00235 00236 def takes_allocator(type): 00237 """ 00238 Returns whether or not a type can take an allocator in its constructor. False for all builtin types except string. 00239 True for all others. 00240 00241 @param type: The type 00242 @type: str 00243 """ 00244 return not type in ['byte', 'int8', 'int16', 'int32', 'int64', 00245 'char', 'uint8', 'uint16', 'uint32', 'uint64', 00246 'float32', 'float64', 'bool', 'time', 'duration'] 00247 00248 def write_initializer_list(s, spec, container_gets_allocator): 00249 """ 00250 Writes the initializer list for a constructor 00251 00252 @param s: The stream to write to 00253 @type s: stream 00254 @param spec: The message spec 00255 @type spec: roslib.msgs.MsgSpec 00256 @param container_gets_allocator: Whether or not a container type (whether it's another message, a vector, array or string) 00257 should have the allocator passed to its constructor. Assumes the allocator is named _alloc. 00258 @type container_gets_allocator: bool 00259 """ 00260 00261 i = 0 00262 for field in spec.parsed_fields(): 00263 if (i == 0): 00264 s.write(' : ') 00265 else: 00266 s.write(' , ') 00267 00268 val = default_value(field.base_type) 00269 use_alloc = takes_allocator(field.base_type) 00270 if (field.is_array): 00271 if (field.array_len is None and container_gets_allocator): 00272 s.write('%s(_alloc)\n'%(field.name)) 00273 else: 00274 s.write('%s()\n'%(field.name)) 00275 else: 00276 if (container_gets_allocator and use_alloc): 00277 s.write('%s(_alloc)\n'%(field.name)) 00278 else: 00279 s.write('%s(%s)\n'%(field.name, val)) 00280 i = i + 1 00281 00282 def write_fixed_length_assigns(s, spec, container_gets_allocator, cpp_name_prefix): 00283 """ 00284 Initialize any fixed-length arrays 00285 00286 @param s: The stream to write to 00287 @type s: stream 00288 @param spec: The message spec 00289 @type spec: roslib.msgs.MsgSpec 00290 @param container_gets_allocator: Whether or not a container type (whether it's another message, a vector, array or string) 00291 should have the allocator passed to its constructor. Assumes the allocator is named _alloc. 00292 @type container_gets_allocator: bool 00293 @param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::" 00294 @type cpp_name_prefix: str 00295 """ 00296 # Assign all fixed-length arrays their default values 00297 for field in spec.parsed_fields(): 00298 if (not field.is_array or field.array_len is None): 00299 continue 00300 00301 val = default_value(field.base_type) 00302 if (container_gets_allocator and takes_allocator(field.base_type)): 00303 # String is a special case, as it is the only builtin type that takes an allocator 00304 if (field.base_type == "string"): 00305 string_cpp = msg_type_to_cpp("string") 00306 s.write(' %s.assign(%s(_alloc));\n'%(field.name, string_cpp)) 00307 else: 00308 (cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, field.base_type) 00309 s.write(' %s.assign(%s(_alloc));\n'%(field.name, cpp_msg_with_alloc)) 00310 elif (len(val) > 0): 00311 s.write(' %s.assign(%s);\n'%(field.name, val)) 00312 00313 def write_constructors(s, spec, cpp_name_prefix): 00314 """ 00315 Writes any necessary constructors for the message 00316 00317 @param s: The stream to write to 00318 @type s: stream 00319 @param spec: The message spec 00320 @type spec: roslib.msgs.MsgSpec 00321 @param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::" 00322 @type cpp_name_prefix: str 00323 """ 00324 00325 msg = spec.short_name 00326 00327 # Default constructor 00328 s.write(' %s_()\n'%(msg)) 00329 write_initializer_list(s, spec, False) 00330 s.write(' {\n') 00331 write_fixed_length_assigns(s, spec, False, cpp_name_prefix) 00332 s.write(' }\n\n') 00333 00334 # Constructor that takes an allocator constructor 00335 s.write(' %s_(const ContainerAllocator& _alloc)\n'%(msg)) 00336 write_initializer_list(s, spec, True) 00337 s.write(' {\n') 00338 write_fixed_length_assigns(s, spec, True, cpp_name_prefix) 00339 s.write(' }\n\n') 00340 00341 def write_member(s, field): 00342 """ 00343 Writes a single member's declaration and type typedef 00344 00345 @param s: The stream to write to 00346 @type s: stream 00347 @param type: The member type 00348 @type type: str 00349 @param name: The name of the member 00350 @type name: str 00351 """ 00352 cpp_type = msg_type_to_cpp(field.type) 00353 s.write(' typedef %s _%s_type;\n'%(cpp_type, field.name)) 00354 s.write(' %s %s;\n\n'%(cpp_type, field.name)) 00355 00356 def write_members(s, spec): 00357 """ 00358 Write all the member declarations 00359 00360 @param s: The stream to write to 00361 @type s: stream 00362 @param spec: The message spec 00363 @type spec: roslib.msgs.MsgSpec 00364 """ 00365 [write_member(s, field) for field in spec.parsed_fields()] 00366 00367 def escape_string(str): 00368 str = str.replace('\\', '\\\\') 00369 str = str.replace('"', '\\"') 00370 return str 00371 00372 def write_constant_declaration(s, constant): 00373 """ 00374 Write a constant value as a static member 00375 00376 @param s: The stream to write to 00377 @type s: stream 00378 @param constant: The constant 00379 @type constant: roslib.msgs.Constant 00380 """ 00381 00382 # integral types get their declarations as enums to allow use at compile time 00383 if (constant.type in ['byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64']): 00384 s.write(' enum { %s = %s };\n'%(constant.name, constant.val)) 00385 else: 00386 s.write(' static const %s %s;\n'%(msg_type_to_cpp(constant.type), constant.name)) 00387 00388 def write_constant_declarations(s, spec): 00389 """ 00390 Write all the constants from a spec as static members 00391 00392 @param s: The stream to write to 00393 @type s: stream 00394 @param spec: The message spec 00395 @type spec: roslib.msgs.MsgSpec 00396 """ 00397 [write_constant_declaration(s, constant) for constant in spec.constants] 00398 s.write('\n') 00399 00400 def write_constant_definition(s, spec, constant): 00401 """ 00402 Write a constant value as a static member 00403 00404 @param s: The stream to write to 00405 @type s: stream 00406 @param constant: The constant 00407 @type constant: roslib.msgs.Constant 00408 """ 00409 00410 # integral types do not need a definition, since they've been defined where they are declared 00411 if (constant.type not in ['byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64', 'string']): 00412 s.write('template<typename ContainerAllocator> const %s %s_<ContainerAllocator>::%s = %s;\n'%(msg_type_to_cpp(constant.type), spec.short_name, constant.name, constant.val)) 00413 elif (constant.type == 'string'): 00414 s.write('template<typename ContainerAllocator> const %s %s_<ContainerAllocator>::%s = "%s";\n'%(msg_type_to_cpp(constant.type), spec.short_name, constant.name, escape_string(constant.val))) 00415 00416 def write_constant_definitions(s, spec): 00417 """ 00418 Write all the constants from a spec as static members 00419 00420 @param s: The stream to write to 00421 @type s: stream 00422 @param spec: The message spec 00423 @type spec: roslib.msgs.MsgSpec 00424 """ 00425 [write_constant_definition(s, spec, constant) for constant in spec.constants] 00426 s.write('\n') 00427 00428 def is_fixed_length(spec): 00429 """ 00430 Returns whether or not the message is fixed-length 00431 00432 @param spec: The message spec 00433 @type spec: roslib.msgs.MsgSpec 00434 @param package: The package of the 00435 @type package: str 00436 """ 00437 types = [] 00438 for field in spec.parsed_fields(): 00439 if (field.is_array and field.array_len is None): 00440 return False 00441 00442 if (field.base_type == 'string'): 00443 return False 00444 00445 if (not field.is_builtin): 00446 types.append(field.base_type) 00447 00448 types = set(types) 00449 for type in types: 00450 type = roslib.msgs.resolve_type(type, spec.package) 00451 (_, new_spec) = roslib.msgs.load_by_type(type, spec.package) 00452 if (not is_fixed_length(new_spec)): 00453 return False 00454 00455 return True 00456 00457 def write_deprecated_member_functions(s, spec, traits): 00458 """ 00459 Writes the deprecated member functions for backwards compatibility 00460 """ 00461 for field in spec.parsed_fields(): 00462 if (field.is_array): 00463 s.write(' ROS_DEPRECATED uint32_t get_%s_size() const { return (uint32_t)%s.size(); }\n'%(field.name, field.name)) 00464 00465 if (field.array_len is None): 00466 s.write(' ROS_DEPRECATED void set_%s_size(uint32_t size) { %s.resize((size_t)size); }\n'%(field.name, field.name)) 00467 s.write(' ROS_DEPRECATED void get_%s_vec(%s& vec) const { vec = this->%s; }\n'%(field.name, msg_type_to_cpp(field.type), field.name)) 00468 s.write(' ROS_DEPRECATED void set_%s_vec(const %s& vec) { this->%s = vec; }\n'%(field.name, msg_type_to_cpp(field.type), field.name)) 00469 00470 for k, v in traits.items(): 00471 s.write('private:\n') 00472 s.write(' static const char* __s_get%s_() { return "%s"; }\n'%(k, v)) 00473 s.write('public:\n') 00474 s.write(' ROS_DEPRECATED static const std::string __s_get%s() { return __s_get%s_(); }\n\n'%(k, k)) 00475 s.write(' ROS_DEPRECATED const std::string __get%s() const { return __s_get%s_(); }\n\n'%(k, k)) 00476 00477 s.write(' ROS_DEPRECATED virtual uint8_t *serialize(uint8_t *write_ptr, uint32_t seq) const\n {\n') 00478 s.write(' ros::serialization::OStream stream(write_ptr, 1000000000);\n') 00479 for field in spec.parsed_fields(): 00480 s.write(' ros::serialization::serialize(stream, %s);\n'%(field.name)) 00481 s.write(' return stream.getData();\n }\n\n') 00482 00483 s.write(' ROS_DEPRECATED virtual uint8_t *deserialize(uint8_t *read_ptr)\n {\n') 00484 s.write(' ros::serialization::IStream stream(read_ptr, 1000000000);\n'); 00485 for field in spec.parsed_fields(): 00486 s.write(' ros::serialization::deserialize(stream, %s);\n'%(field.name)) 00487 s.write(' return stream.getData();\n }\n\n') 00488 00489 s.write(' ROS_DEPRECATED virtual uint32_t serializationLength() const\n {\n') 00490 s.write(' uint32_t size = 0;\n'); 00491 for field in spec.parsed_fields(): 00492 s.write(' size += ros::serialization::serializationLength(%s);\n'%(field.name)) 00493 s.write(' return size;\n }\n\n') 00494 00495 def compute_full_text_escaped(gen_deps_dict): 00496 """ 00497 Same as roslib.gentools.compute_full_text, except that the 00498 resulting text is escaped to be safe for C++ double quotes 00499 00500 @param get_deps_dict: dictionary returned by get_dependencies call 00501 @type get_deps_dict: dict 00502 @return: concatenated text for msg/srv file and embedded msg/srv types. Text will be escaped for double quotes 00503 @rtype: str 00504 """ 00505 definition = roslib.gentools.compute_full_text(gen_deps_dict) 00506 lines = definition.split('\n') 00507 s = StringIO() 00508 for line in lines: 00509 line = escape_string(line) 00510 s.write('%s\\n\\\n'%(line)) 00511 00512 val = s.getvalue() 00513 s.close() 00514 return val 00515 00516 def is_hex_string(str): 00517 for c in str: 00518 if c not in '0123456789abcdefABCDEF': 00519 return False 00520 00521 return True 00522 00523 def write_trait_char_class(s, class_name, cpp_msg_with_alloc, value, write_static_hex_value = False): 00524 """ 00525 Writes a class trait for traits which have static value() members that return const char* 00526 00527 e.g. write_trait_char_class(s, "MD5Sum", "std_msgs::String_<ContainerAllocator>", "hello") yields: 00528 template<class ContainerAllocator> 00529 struct MD5Sum<std_msgs::String_<ContainerAllocator> > 00530 { 00531 static const char* value() { return "hello"; } 00532 static const char* value(const std_msgs::String_<ContainerAllocator>&) { return value(); } 00533 }; 00534 00535 @param s: The stream to write to 00536 @type s: stream 00537 @param class_name: The name of the trait class to write 00538 @type class_name: str 00539 @param cpp_msg_with_alloc: The C++ declaration of the message, including the allocator template 00540 @type cpp_msg_with_alloc: str 00541 @param value: The value to return in the string 00542 @type value: str 00543 @param write_static_hex_value: Whether or not to write a set of compile-time-checkable static values. Useful for, 00544 for example, MD5Sum. Writes static const uint64_t static_value1... static_valueN 00545 @type write_static_hex_value: bool 00546 @raise ValueError if write_static_hex_value is True but value contains characters invalid in a hex value 00547 """ 00548 s.write('template<class ContainerAllocator>\nstruct %s<%s> {\n'%(class_name, cpp_msg_with_alloc)) 00549 s.write(' static const char* value() \n {\n return "%s";\n }\n\n'%(value)) 00550 s.write(' static const char* value(const %s&) { return value(); } \n'%(cpp_msg_with_alloc)) 00551 if (write_static_hex_value): 00552 if (not is_hex_string(value)): 00553 raise ValueError('%s is not a hex value'%(value)) 00554 00555 iter_count = len(value) / 16 00556 for i in range(0, int(iter_count)): 00557 start = i*16 00558 s.write(' static const uint64_t static_value%s = 0x%sULL;\n'%((i+1), value[start:start+16])) 00559 s.write('};\n\n') 00560 00561 def write_trait_true_class(s, class_name, cpp_msg_with_alloc): 00562 """ 00563 Writes a true/false trait class 00564 00565 @param s: stream to write to 00566 @type s: stream 00567 @param class_name: Name of the trait class 00568 @type class_name: str 00569 @param cpp_msg_with_alloc: The C++ declaration of the message, including the allocator template 00570 @type cpp_msg_with_alloc: str 00571 """ 00572 s.write('template<class ContainerAllocator> struct %s<%s> : public TrueType {};\n'%(class_name, cpp_msg_with_alloc)) 00573 00574 def write_traits(s, spec, cpp_name_prefix, datatype = None): 00575 """ 00576 Writes all the traits classes for a message 00577 00578 @param s: The stream to write to 00579 @type s: stream 00580 @param spec: The message spec 00581 @type spec: roslib.msgs.MsgSpec 00582 @param cpp_name_prefix: The C++ prefix to prepend to a message to refer to it (e.g. "std_msgs::") 00583 @type cpp_name_prefix: str 00584 @param datatype: The string to write as the datatype of the message. If None (default), pkg/msg is used. 00585 @type datatype: str 00586 """ 00587 # generate dependencies dictionary 00588 gendeps_dict = roslib.gentools.get_dependencies(spec, spec.package, compute_files=False) 00589 md5sum = roslib.gentools.compute_md5(gendeps_dict) 00590 full_text = compute_full_text_escaped(gendeps_dict) 00591 00592 if (datatype is None): 00593 datatype = '%s'%(spec.full_name) 00594 00595 (cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, spec.short_name) 00596 s.write('namespace ros\n{\n') 00597 s.write('namespace message_traits\n{\n') 00598 00599 write_trait_true_class(s, 'IsMessage', cpp_msg_with_alloc) 00600 write_trait_true_class(s, 'IsMessage', cpp_msg_with_alloc + " const") 00601 00602 write_trait_char_class(s, 'MD5Sum', cpp_msg_with_alloc, md5sum, True) 00603 write_trait_char_class(s, 'DataType', cpp_msg_with_alloc, datatype) 00604 write_trait_char_class(s, 'Definition', cpp_msg_with_alloc, full_text) 00605 00606 if (spec.has_header()): 00607 write_trait_true_class(s, 'HasHeader', cpp_msg_with_alloc) 00608 write_trait_true_class(s, 'HasHeader', ' const' + cpp_msg_with_alloc) 00609 00610 if (is_fixed_length(spec)): 00611 write_trait_true_class(s, 'IsFixedSize', cpp_msg_with_alloc) 00612 00613 s.write('} // namespace message_traits\n') 00614 s.write('} // namespace ros\n\n') 00615 00616 def write_operations(s, spec, cpp_name_prefix): 00617 (cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, spec.short_name) 00618 s.write('namespace ros\n{\n') 00619 s.write('namespace message_operations\n{\n') 00620 00621 # Write the Printer operation 00622 s.write('\ntemplate<class ContainerAllocator>\nstruct Printer<%s>\n{\n'%(cpp_msg_with_alloc)) 00623 s.write(' template<typename Stream> static void stream(Stream& s, const std::string& indent, const %s& v) \n {\n'%cpp_msg_with_alloc) 00624 for field in spec.parsed_fields(): 00625 cpp_type = msg_type_to_cpp(field.base_type) 00626 if (field.is_array): 00627 s.write(' s << indent << "%s[]" << std::endl;\n'%(field.name)) 00628 s.write(' for (size_t i = 0; i < v.%s.size(); ++i)\n {\n'%(field.name)) 00629 s.write(' s << indent << " %s[" << i << "]: ";\n'%field.name) 00630 indent_increment = ' ' 00631 if (not field.is_builtin): 00632 s.write(' s << std::endl;\n') 00633 s.write(' s << indent;\n') 00634 indent_increment = ' '; 00635 s.write(' Printer<%s>::stream(s, indent + "%s", v.%s[i]);\n'%(cpp_type, indent_increment, field.name)) 00636 s.write(' }\n') 00637 else: 00638 s.write(' s << indent << "%s: ";\n'%field.name) 00639 indent_increment = ' ' 00640 if (not field.is_builtin or field.is_array): 00641 s.write('s << std::endl;\n') 00642 s.write(' Printer<%s>::stream(s, indent + "%s", v.%s);\n'%(cpp_type, indent_increment, field.name)) 00643 s.write(' }\n') 00644 s.write('};\n\n') 00645 00646 s.write('\n') 00647 00648 s.write('} // namespace message_operations\n') 00649 s.write('} // namespace ros\n\n') 00650 00651 def write_serialization(s, spec, cpp_name_prefix): 00652 """ 00653 Writes the Serializer class for a message 00654 00655 @param s: Stream to write to 00656 @type s: stream 00657 @param spec: The message spec 00658 @type spec: roslib.msgs.MsgSpec 00659 @param cpp_name_prefix: The C++ prefix to prepend to a message to refer to it (e.g. "std_msgs::") 00660 @type cpp_name_prefix: str 00661 """ 00662 (cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, spec.short_name) 00663 00664 s.write('namespace ros\n{\n') 00665 s.write('namespace serialization\n{\n\n') 00666 00667 s.write('template<class ContainerAllocator> struct Serializer<%s>\n{\n'%(cpp_msg_with_alloc)) 00668 00669 s.write(' template<typename Stream, typename T> inline static void allInOne(Stream& stream, T m)\n {\n') 00670 for field in spec.parsed_fields(): 00671 s.write(' stream.next(m.%s);\n'%(field.name)) 00672 s.write(' }\n\n') 00673 00674 s.write(' ROS_DECLARE_ALLINONE_SERIALIZER;\n') 00675 00676 s.write('}; // struct %s_\n'%(spec.short_name)) 00677 00678 s.write('} // namespace serialization\n') 00679 s.write('} // namespace ros\n\n') 00680 00681 def write_ostream_operator(s, spec, cpp_name_prefix): 00682 (cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, spec.short_name) 00683 s.write('template<typename ContainerAllocator>\nstd::ostream& operator<<(std::ostream& s, const %s& v)\n{\n'%(cpp_msg_with_alloc)) 00684 s.write(' ros::message_operations::Printer<%s>::stream(s, "", v);\n return s;}\n\n'%(cpp_msg_with_alloc)) 00685 00686 def generate(msg_path): 00687 """ 00688 Generate a message 00689 00690 @param msg_path: The path to the .msg file 00691 @type msg_path: str 00692 """ 00693 (package_dir, package) = roslib.packages.get_dir_pkg(msg_path) 00694 (_, spec) = roslib.msgs.load_from_file(msg_path, package) 00695 00696 s = StringIO() 00697 write_begin(s, spec, msg_path) 00698 write_generic_includes(s) 00699 write_includes(s, spec) 00700 00701 cpp_prefix = '%s::'%(package) 00702 00703 s.write('namespace %s\n{\n'%(package)) 00704 write_struct(s, spec, cpp_prefix) 00705 write_constant_definitions(s, spec) 00706 write_ostream_operator(s, spec, cpp_prefix) 00707 s.write('} // namespace %s\n\n'%(package)) 00708 00709 write_traits(s, spec, cpp_prefix) 00710 write_serialization(s, spec, cpp_prefix) 00711 write_operations(s, spec, cpp_prefix) 00712 00713 # HACK HACK HACK. The moving of roslib/Header causes many problems. We end up having to make roslib/Header act exactly 00714 # like std_msgs/Header (as in, constructor that takes it, as well as operator std_msgs::Header()), and it needs to be 00715 # available wherever std_msgs/Header.h has been included 00716 if (package == "std_msgs" and spec.short_name == "Header"): 00717 s.write("#define STD_MSGS_INCLUDING_HEADER_DEPRECATED_DEF 1\n") 00718 s.write("#include <std_msgs/header_deprecated_def.h>\n") 00719 s.write("#undef STD_MSGS_INCLUDING_HEADER_DEPRECATED_DEF\n\n") 00720 00721 write_end(s, spec) 00722 00723 output_dir = '%s/msg_gen/cpp/include/%s'%(package_dir, package) 00724 if (not os.path.exists(output_dir)): 00725 # if we're being run concurrently, the above test can report false but os.makedirs can still fail if 00726 # another copy just created the directory 00727 try: 00728 os.makedirs(output_dir) 00729 except OSError as e: 00730 pass 00731 00732 f = open('%s/%s.h'%(output_dir, spec.short_name), 'w') 00733 f.write(s.getvalue() + "\n") 00734 00735 s.close() 00736 00737 def generate_messages(argv): 00738 for arg in argv[1:]: 00739 generate(arg) 00740 00741 if __name__ == "__main__": 00742 roslib.msgs.set_verbose(False) 00743 generate_messages(sys.argv) 00744