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


roscpp
Author(s): Morgan Quigley, Josh Faust, Brian Gerkey, Troy Straszheim
autogenerated on Thu Jun 6 2019 21:10:05