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     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, rospack=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, rospack=rospack)
00589     md5sum = roslib.gentools.compute_md5(gendeps_dict, rospack=rospack)
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     rospack = RosPack()
00710     write_traits(s, spec, cpp_prefix, rospack=rospack)
00711     write_serialization(s, spec, cpp_prefix)
00712     write_operations(s, spec, cpp_prefix)
00713     
00714     # HACK HACK HACK.  The moving of roslib/Header causes many problems.  We end up having to make roslib/Header act exactly
00715     # like std_msgs/Header (as in, constructor that takes it, as well as operator std_msgs::Header()), and it needs to be
00716     # available wherever std_msgs/Header.h has been included
00717     if (package == "std_msgs" and spec.short_name == "Header"):
00718         s.write("#define STD_MSGS_INCLUDING_HEADER_DEPRECATED_DEF 1\n")
00719         s.write("#include <std_msgs/header_deprecated_def.h>\n")
00720         s.write("#undef STD_MSGS_INCLUDING_HEADER_DEPRECATED_DEF\n\n") 
00721     
00722     write_end(s, spec)
00723     
00724     output_dir = '%s/msg_gen/cpp/include/%s'%(package_dir, package)
00725     if (not os.path.exists(output_dir)):
00726         # if we're being run concurrently, the above test can report false but os.makedirs can still fail if
00727         # another copy just created the directory
00728         try:
00729             os.makedirs(output_dir)
00730         except OSError as e:
00731             pass
00732          
00733     f = open('%s/%s.h'%(output_dir, spec.short_name), 'w')
00734     f.write(s.getvalue() + "\n")
00735     
00736     s.close()
00737 
00738 def generate_messages(argv):
00739     for arg in argv[1:]:
00740         generate(arg)
00741 
00742 if __name__ == "__main__":
00743     roslib.msgs.set_verbose(False)
00744     generate_messages(sys.argv)
00745 


roscpp
Author(s): Morgan Quigley, Josh Faust, Brian Gerkey, Troy Straszheim
autogenerated on Fri Aug 28 2015 12:33:10