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