├── .gitignore ├── CHANGELOG.rst ├── CMakeLists.txt ├── README.md ├── cmake └── gencpp-extras.cmake.em ├── package.xml ├── scripts ├── CMakeLists.txt ├── gen_cpp.py ├── msg.h.template └── srv.h.template ├── setup.py └── src └── gencpp └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | ._* 3 | *~ 4 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package gencpp 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.7.2 (2025-04-25) 6 | ------------------ 7 | * Fix msg printer for arrays (`#58 `_) 8 | * Contributors: Robert Haschke 9 | 10 | 0.7.1 (2025-04-10) 11 | ------------------ 12 | * Fix usage of deprecated std::allocator::rebind (`#51 `_) 13 | * Remove unnecessary map include (`#48 `_) 14 | * Contributors: Markus Vieth, poggenhans 15 | 16 | 0.6.5 (2020-03-03) 17 | ------------------ 18 | * add operator== & operator!= to message generation (`#41 `_) 19 | 20 | 0.6.4 (2020-03-02) 21 | ------------------ 22 | * [windows] reducing the odds to have name collisions (`#47 `_) 23 | 24 | 0.6.3 (2020-01-24) 25 | ------------------ 26 | * various code cleanup (`#46 `_) 27 | * package.xml format 3 (`#45 `_) 28 | * use setuptools instead of distutils (`#43 `_) 29 | * bump CMake version to avoid CMP0048 warning (`#44 `_) 30 | * two patches to make the generated headers reproducible (`#42 `_) 31 | 32 | 0.6.2 (2019-03-18) 33 | ------------------ 34 | * add plugins the ability to also generate free functions (`#40 `_) 35 | 36 | 0.6.1 (2019-03-04) 37 | ------------------ 38 | * enable Windows build (`#38 `_) 39 | 40 | 0.6.0 (2018-01-29) 41 | ------------------ 42 | * add plugin support for generated C++ message headers (`#32 `_) 43 | * put all the message integer constants into a common enum (`#25 `_) 44 | 45 | 0.5.5 (2016-06-27) 46 | ------------------ 47 | * fix extra semicolon warning (`#26 `_) 48 | 49 | 0.5.4 (2016-03-14) 50 | ------------------ 51 | * fix unused parameter warning (`#24 `_) 52 | 53 | 0.5.3 (2014-12-22) 54 | ------------------ 55 | * remove copyright header from generated code (`#20 `_) 56 | 57 | 0.5.2 (2014-05-07) 58 | ------------------ 59 | * add architecture_independent flag in package.xml (`#19 `_) 60 | 61 | 0.5.1 (2014-02-24) 62 | ------------------ 63 | * use catkin_install_python() to install Python scripts (`#18 `_) 64 | * add 'u' suffix to unsigned enum values to avoid compiler warning (`#16 `_) 65 | 66 | 0.5.0 (2014-01-29) 67 | ------------------ 68 | * remove __connection_header from message template (`#3 `_) 69 | 70 | 0.4.16 (2014-01-27) 71 | ------------------- 72 | * fix warning about empty message definition (`ros/ros_comm#344 `_) 73 | 74 | 0.4.15 (2014-01-07) 75 | ------------------- 76 | * python 3 compatibility 77 | * fix generated code of message definition with windows line endings (`#6 `_) 78 | 79 | 0.4.14 (2013-08-21) 80 | ------------------- 81 | * make gencpp relocatable (`ros/catkin#490 `_) 82 | 83 | 0.4.13 (2013-06-18) 84 | ------------------- 85 | * update message targets to depend on template 86 | * update msg template to generate empty functions without warnings about unused variables (`#4 `_) 87 | 88 | 0.4.12 (2013-03-08) 89 | ------------------- 90 | * fix handling spaces in folder names (`ros/catkin#375 `_) 91 | 92 | 0.4.11 (2012-12-21) 93 | ------------------- 94 | * first public release for Groovy 95 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(gencpp) 3 | find_package(catkin REQUIRED COMPONENTS genmsg) 4 | 5 | catkin_package( 6 | CATKIN_DEPENDS genmsg 7 | CFG_EXTRAS gencpp-extras.cmake 8 | ) 9 | 10 | add_subdirectory(scripts) 11 | 12 | file(WRITE ${CATKIN_DEVEL_PREFIX}/${GENMSG_LANGS_DESTINATION}/gencpp "C++") 13 | install(FILES ${CATKIN_DEVEL_PREFIX}/${GENMSG_LANGS_DESTINATION}/gencpp 14 | DESTINATION ${GENMSG_LANGS_DESTINATION}) 15 | 16 | catkin_python_setup() 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archived - ROS 1 End-of-life 2 | 3 | This repository contains ROS 1 packages. 4 | The last supported ROS 1 release, ROS Noetic, [officially reached end of life on May 31st, 2025](https://bit.ly/NoeticEOL). 5 | 6 | -------------------------------------------------------------------------------- /cmake/gencpp-extras.cmake.em: -------------------------------------------------------------------------------- 1 | @[if DEVELSPACE]@ 2 | # bin and template dir variables in develspace 3 | set(GENCPP_BIN "@(CMAKE_CURRENT_SOURCE_DIR)/scripts/gen_cpp.py") 4 | set(GENCPP_TEMPLATE_DIR "@(CMAKE_CURRENT_SOURCE_DIR)/scripts") 5 | @[else]@ 6 | # bin and template dir variables in installspace 7 | set(GENCPP_BIN "${gencpp_DIR}/../../../@(CATKIN_PACKAGE_BIN_DESTINATION)/gen_cpp.py") 8 | set(GENCPP_TEMPLATE_DIR "${gencpp_DIR}/..") 9 | @[end if]@ 10 | 11 | # Generate .msg->.h for cpp 12 | # The generated .h files should be added ALL_GEN_OUTPUT_FILES_cpp 13 | macro(_generate_msg_cpp ARG_PKG ARG_MSG ARG_IFLAGS ARG_MSG_DEPS ARG_GEN_OUTPUT_DIR) 14 | file(MAKE_DIRECTORY ${ARG_GEN_OUTPUT_DIR}) 15 | 16 | #Create input and output filenames 17 | get_filename_component(MSG_NAME ${ARG_MSG} NAME) 18 | get_filename_component(MSG_SHORT_NAME ${ARG_MSG} NAME_WE) 19 | 20 | set(MSG_GENERATED_NAME ${MSG_SHORT_NAME}.h) 21 | set(GEN_OUTPUT_FILE ${ARG_GEN_OUTPUT_DIR}/${MSG_GENERATED_NAME}) 22 | 23 | # check if a user-provided header file exists 24 | if(EXISTS "${PROJECT_SOURCE_DIR}/include/${ARG_PKG}/${MSG_SHORT_NAME}.h") 25 | message(STATUS "${ARG_PKG}: Found user-provided header '${PROJECT_SOURCE_DIR}/include/${ARG_PKG}/${MSG_SHORT_NAME}.h' for message '${ARG_PKG}/${MSG_SHORT_NAME}'. Skipping generation...") 26 | # Do nothing. The header will be installed by the user. 27 | else() 28 | # check if a user-provided plugin header file exists 29 | if(EXISTS "${PROJECT_SOURCE_DIR}/include/${ARG_PKG}/plugin/${MSG_SHORT_NAME}.h") 30 | message(STATUS "${ARG_PKG}: Found user-provided plugin header '${PROJECT_SOURCE_DIR}/include/${ARG_PKG}/plugin/${MSG_SHORT_NAME}.h' for message '${ARG_PKG}/${MSG_SHORT_NAME}'.") 31 | # Add a file dependency to enforce regeneration if the plugin header was added after initial cmake invocation. 32 | # Even with --force-cmake the generator would otherwise not run if the .msg file did not change. 33 | set(MSG_PLUGIN "${PROJECT_SOURCE_DIR}/include/${ARG_PKG}/plugin/${MSG_SHORT_NAME}.h") 34 | else() 35 | set(MSG_PLUGIN) 36 | endif() 37 | 38 | assert(CATKIN_ENV) 39 | add_custom_command(OUTPUT ${GEN_OUTPUT_FILE} 40 | DEPENDS ${GENCPP_BIN} ${ARG_MSG} ${ARG_MSG_DEPS} ${MSG_PLUGIN} "${GENCPP_TEMPLATE_DIR}/msg.h.template" ${ARGN} 41 | COMMAND ${CATKIN_ENV} ${PYTHON_EXECUTABLE} ${GENCPP_BIN} ${ARG_MSG} 42 | ${ARG_IFLAGS} 43 | -p ${ARG_PKG} 44 | -o ${ARG_GEN_OUTPUT_DIR} 45 | -e ${GENCPP_TEMPLATE_DIR} 46 | COMMENT "Generating C++ code from ${ARG_PKG}/${MSG_NAME}" 47 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 48 | ) 49 | list(APPEND ALL_GEN_OUTPUT_FILES_cpp ${GEN_OUTPUT_FILE}) 50 | endif() 51 | 52 | gencpp_append_include_dirs() 53 | endmacro() 54 | 55 | #gencpp uses the same program to generate srv and msg files, so call the same macro 56 | macro(_generate_srv_cpp ARG_PKG ARG_SRV ARG_IFLAGS ARG_MSG_DEPS ARG_GEN_OUTPUT_DIR) 57 | _generate_msg_cpp(${ARG_PKG} ${ARG_SRV} "${ARG_IFLAGS}" "${ARG_MSG_DEPS}" ${ARG_GEN_OUTPUT_DIR} "${GENCPP_TEMPLATE_DIR}/srv.h.template") 58 | endmacro() 59 | 60 | macro(_generate_module_cpp) 61 | # the macros, they do nothing 62 | endmacro() 63 | 64 | set(gencpp_INSTALL_DIR include) 65 | 66 | macro(gencpp_append_include_dirs) 67 | if(NOT gencpp_APPENDED_INCLUDE_DIRS) 68 | # make sure we can find generated messages and that they overlay all other includes 69 | include_directories(BEFORE ${CATKIN_DEVEL_PREFIX}/${gencpp_INSTALL_DIR}) 70 | # pass the include directory to catkin_package() 71 | list(APPEND ${PROJECT_NAME}_INCLUDE_DIRS ${CATKIN_DEVEL_PREFIX}/${gencpp_INSTALL_DIR}) 72 | set(gencpp_APPENDED_INCLUDE_DIRS TRUE) 73 | endif() 74 | endmacro() 75 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | gencpp 7 | 0.7.2 8 | C++ ROS message and service generators. 9 | Dirk Thomas 10 | BSD 11 | 12 | https://github.com/ros/gencpp/issues 13 | https://github.com/ros/gencpp 14 | 15 | Josh Faust 16 | Troy Straszheim 17 | Morgen Kjaergaard 18 | 19 | catkin 20 | python-setuptools 21 | python3-setuptools 22 | 23 | genmsg 24 | 25 | genmsg 26 | 27 | 28 | cpp 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /scripts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install( 2 | FILES msg.h.template srv.h.template 3 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) 4 | 5 | catkin_install_python( 6 | PROGRAMS gen_cpp.py 7 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 8 | -------------------------------------------------------------------------------- /scripts/gen_cpp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Software License Agreement (BSD License) 3 | # 4 | # Copyright (c) 2009, Willow Garage, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # * Neither the name of Willow Garage, Inc. nor the names of its 18 | # contributors may be used to endorse or promote products derived 19 | # from this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | # 34 | 35 | """ 36 | ROS message source code generation for C++. 37 | 38 | Converts ROS .msg files in a package into C++ source code implementations. 39 | """ 40 | 41 | import sys 42 | 43 | import genmsg.template_tools 44 | 45 | msg_template_map = {'msg.h.template': '@NAME@.h'} 46 | srv_template_map = {'srv.h.template': '@NAME@.h'} 47 | 48 | if __name__ == '__main__': 49 | genmsg.template_tools.generate_from_command_line_options( 50 | sys.argv, msg_template_map, srv_template_map) 51 | -------------------------------------------------------------------------------- /scripts/msg.h.template: -------------------------------------------------------------------------------- 1 | @############################################### 2 | @# 3 | @# ROS message source code generation for C++ 4 | @# 5 | @# EmPy template for generating .h files 6 | @# 7 | @############################################### 8 | @# Start of Template 9 | @# 10 | @# Context: 11 | @# - file_name_in (String) Source file 12 | @# - spec (msggen.MsgSpec) Parsed specification of the .msg file 13 | @# - md5sum (String) MD5Sum of the .msg specification 14 | @############################################### 15 | // Generated by gencpp from file @(spec.package)/@(spec.short_name).msg 16 | // DO NOT EDIT! 17 | 18 | @{ 19 | from collections import OrderedDict 20 | import genmsg.msgs 21 | import gencpp 22 | import os 23 | 24 | cpp_namespace = '::%s::'%(spec.package) # TODO handle nested namespace 25 | cpp_class = '%s_'%spec.short_name 26 | cpp_full_name = '%s%s'%(cpp_namespace,cpp_class) 27 | cpp_full_name_with_alloc = '%s'%(cpp_full_name) 28 | cpp_full_name_with_comp_alloc1 = '%s'%(cpp_full_name) 29 | cpp_full_name_with_comp_alloc2 = '%s'%(cpp_full_name) 30 | cpp_msg_definition = gencpp.escape_message_definition(msg_definition) 31 | has_plugin = os.path.exists('include/%s/plugin/%s.h' % (spec.package, spec.short_name)) 32 | has_plugin_after = os.path.exists('include/%s/plugin/%s.after.h' % (spec.package, spec.short_name)) 33 | }@ 34 | 35 | #ifndef @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_H 36 | #define @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_H 37 | 38 | @############################## 39 | @# Generic Includes 40 | @############################## 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | @############################## 52 | @# Includes for dependencies 53 | @############################## 54 | @{ 55 | for field in spec.parsed_fields(): 56 | if (not field.is_builtin): 57 | if (field.is_header): 58 | print('#include ') 59 | else: 60 | (package, name) = genmsg.names.package_resource_name(field.base_type) 61 | package = package or spec.package # convert '' to package 62 | print('#include <%s/%s.h>'%(package, name)) 63 | }@ 64 | @############################## 65 | @# Plugin 66 | @############################## 67 | @[if has_plugin]@ 68 | #include <@(spec.package)/plugin/@(spec.short_name).h> 69 | @[end if]@ 70 | 71 | namespace @(spec.package) 72 | { 73 | template 74 | struct @(spec.short_name)_ 75 | { 76 | typedef @(spec.short_name)_ Type; 77 | 78 | @# constructors (with and without allocator) 79 | @[if has_plugin]@ 80 | #ifdef @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_CONSTRUCTOR 81 | @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_CONSTRUCTOR 82 | #else 83 | @[end if]@ 84 | @[for (alloc_type,alloc_name) in [['',''],['const ContainerAllocator& ','_alloc']]]@ 85 | @(spec.short_name)_(@(alloc_type+alloc_name)) 86 | @# Write initializer list 87 | @('\n '.join(gencpp.generate_initializer_list(spec, alloc_name != '' )))@ 88 | { 89 | @# Fixed length arrays 90 | @[if alloc_name != '' and not [f for f in spec.parsed_fields() if f.is_array and f.array_len is not None and f.base_type == 'string']]@ 91 | (void)_alloc; 92 | @[end if]@ 93 | @('\n '.join(gencpp.generate_fixed_length_assigns(spec, alloc_name != '', '%s::'%(spec.package))))@ 94 | } 95 | @[end for] 96 | @[if has_plugin]@ 97 | #endif 98 | @[end if]@ 99 | 100 | @[for field in spec.parsed_fields()] 101 | @{cpp_type = gencpp.msg_type_to_cpp(field.type)}@ 102 | typedef @(cpp_type) _@(field.name)_type; 103 | _@(field.name)_type @(field.name); 104 | @[end for] 105 | 106 | @# Constants 107 | @{ 108 | constants_integer = [] 109 | constants_non_integer = [] 110 | for constant in spec.constants: 111 | if constant.type in ['byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64']: 112 | constants_integer.append(constant) 113 | else: 114 | constants_non_integer.append(constant) 115 | }@ 116 | 117 | @[if len(spec.constants) > 0]@ 118 | // reducing the odds to have name collisions with Windows.h 119 | @[for constant in spec.constants]@ 120 | #if defined(_WIN32) && defined(@(constant.name)) 121 | #undef @(constant.name) 122 | #endif 123 | @[end for]@ 124 | 125 | @[end if]@ 126 | @# Integer constants 127 | @[if len(constants_integer) > 0]@ 128 | enum { 129 | @[for constant in constants_integer]@ 130 | @[if (constant.type in ['byte', 'int8', 'int16', 'int32', 'int64', 'char'])]@ 131 | @(constant.name) = @(int(constant.val)), 132 | @[elif (constant.type in ['uint8', 'uint16', 'uint32', 'uint64'])]@ 133 | @(constant.name) = @(int(constant.val))u, 134 | @[end if]@ 135 | @[end for]@ 136 | }; 137 | @[end if]@ 138 | 139 | @# Non-integer constants 140 | @[for constant in constants_non_integer]@ 141 | static const @(gencpp.msg_type_to_cpp(constant.type)) @(constant.name); 142 | @[end for]@ 143 | 144 | @# Shared pointer typedefs 145 | typedef boost::shared_ptr< @(cpp_full_name) > Ptr; 146 | typedef boost::shared_ptr< @(cpp_full_name) const> ConstPtr; 147 | 148 | @# Class body extensions provided by plugin 149 | @[if has_plugin]@ 150 | #ifdef @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_CLASS_BODY 151 | @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_CLASS_BODY 152 | #endif 153 | @[end if]@ 154 | }; // struct @(cpp_class) 155 | 156 | @# Typedef of template instance using std::allocator 157 | typedef @(cpp_full_name) > @(spec.short_name); 158 | 159 | @# Shared pointer typedefs 160 | typedef boost::shared_ptr< @(cpp_namespace+spec.short_name) > @(spec.short_name)Ptr; 161 | typedef boost::shared_ptr< @(cpp_namespace+spec.short_name) const> @(spec.short_name)ConstPtr; 162 | 163 | // constants requiring out of line definition 164 | @[for c in spec.constants] 165 | @[if c.type not in ['byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64']] 166 | template const @(gencpp.msg_type_to_cpp(c.type)) 167 | @(spec.short_name)_::@(c.name) = 168 | @[if c.type == 'string'] 169 | "@(gencpp.escape_string(c.val))" 170 | @[elif c.type == 'bool'] 171 | @(int(c.val)) 172 | @[else] 173 | @c.val 174 | @[end if] 175 | ; 176 | @[end if] 177 | @[end for] 178 | 179 | 180 | @# Printer 181 | template 182 | std::ostream& operator<<(std::ostream& s, const @(cpp_full_name_with_alloc) & v) 183 | { 184 | ros::message_operations::Printer< @(cpp_full_name_with_alloc) >::stream(s, "", v); 185 | return s; 186 | } 187 | 188 | @[if len(spec.parsed_fields()) > 0] 189 | @# Equality 190 | template 191 | bool operator==(const @(cpp_full_name_with_comp_alloc1) & lhs, const @(cpp_full_name_with_comp_alloc2) & rhs) 192 | { 193 | return @ 194 | @[for i, field in enumerate(spec.parsed_fields())]@ 195 | @[if i != 0]@ 196 | && 197 | @ 198 | @[end if]@ 199 | lhs.@(field.name) == rhs.@(field.name)@ 200 | @[end for]@ 201 | ; 202 | } 203 | 204 | @# Inequality 205 | template 206 | bool operator!=(const @(cpp_full_name_with_comp_alloc1) & lhs, const @(cpp_full_name_with_comp_alloc2) & rhs) 207 | { 208 | return !(lhs == rhs); 209 | } 210 | 211 | @[end if] 212 | @# End of namespace 213 | } // namespace @(spec.package) 214 | 215 | @# Message Traits 216 | namespace ros 217 | { 218 | namespace message_traits 219 | { 220 | 221 | @{ 222 | bool_traits = OrderedDict( 223 | IsMessage=True, 224 | IsFixedSize=gencpp.is_fixed_length(spec, msg_context, search_path), 225 | HasHeader=spec.has_header(), 226 | ) 227 | def booltotype(b): 228 | return "TrueType" if b else "FalseType" 229 | } 230 | 231 | @# Binary traits 232 | 233 | @[for k, v in bool_traits.items()]@ 234 | 235 | template 236 | struct @(k)< @(cpp_full_name_with_alloc) > 237 | : @(booltotype(v)) 238 | { }; 239 | 240 | template 241 | struct @(k)< @(cpp_full_name_with_alloc) const> 242 | : @(booltotype(v)) 243 | { }; 244 | @[end for]@ 245 | 246 | @# String traits 247 | @[for trait_class,trait_value in [['MD5Sum', md5sum], ['DataType', spec.full_name], ['Definition', cpp_msg_definition]]]@ 248 | 249 | template 250 | struct @(trait_class)< @(cpp_full_name_with_alloc) > 251 | { 252 | static const char* value() 253 | { 254 | @[if trait_class == 'Definition']@ 255 | return @(trait_value); 256 | @[else]@ 257 | return "@(trait_value)"; 258 | @[end if]@ 259 | } 260 | 261 | static const char* value(const @(cpp_full_name_with_alloc)&) { return value(); } 262 | @{ 263 | if trait_class == 'MD5Sum': 264 | iter_count = int(len(trait_value) / 16) 265 | for i in range(0, iter_count): 266 | start = i*16 267 | print(' static const uint64_t static_value%s = 0x%sULL;'%((i+1), trait_value[start:start+16])) 268 | }@ 269 | }; 270 | @[end for]@ 271 | 272 | @# End of traits 273 | } // namespace message_traits 274 | } // namespace ros 275 | 276 | @# Serialization 277 | namespace ros 278 | { 279 | namespace serialization 280 | { 281 | 282 | @[if has_plugin]@ 283 | #ifdef @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_SERIALIZER 284 | @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_SERIALIZER 285 | #else 286 | @[end if]@ 287 | template struct Serializer< @(cpp_full_name_with_alloc) > 288 | { 289 | @[if spec.parsed_fields()]@ 290 | template inline static void allInOne(Stream& stream, T m) 291 | { 292 | @[for field in spec.parsed_fields()]@ 293 | stream.next(m.@(field.name)); 294 | @[end for]@ 295 | } 296 | @[else]@ 297 | template inline static void allInOne(Stream&, T) 298 | {} 299 | @[end if]@ 300 | 301 | ROS_DECLARE_ALLINONE_SERIALIZER 302 | }; // struct @(cpp_class) 303 | @[if has_plugin]@ 304 | #endif 305 | @[end if]@ 306 | 307 | } // namespace serialization 308 | } // namespace ros 309 | 310 | @# Message Operations 311 | namespace ros 312 | { 313 | namespace message_operations 314 | { 315 | 316 | @# Printer operation 317 | @[if has_plugin]@ 318 | #ifdef @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_PRINTER 319 | @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_PLUGIN_PRINTER 320 | #else 321 | @[end if]@ 322 | template 323 | struct Printer< @(cpp_full_name_with_alloc) > 324 | { 325 | @[if spec.parsed_fields()]@ 326 | template static void stream(Stream& s, const std::string& indent, const @(cpp_full_name_with_alloc)& v) 327 | { 328 | @# todo, change this stuff below into proper EmPy syntax 329 | @{ 330 | for i, field in enumerate(spec.parsed_fields()): 331 | cpp_type = gencpp.msg_type_to_cpp(field.base_type) 332 | inline = 'true' if field.is_builtin else 'false' 333 | # Print new line for every field except first top-level one 334 | print(' if (%s || !indent.empty())'%('true' if i > 0 else 'false')) 335 | print(' s << std::endl;') 336 | print(' s << indent << "%s: ";'%(field.name)) 337 | if (field.is_array): 338 | print(' if (v.%s.empty() || %s)'%(field.name, inline)) 339 | print(' s << "[";') 340 | print(' for (size_t i = 0; i < v.%s.size(); ++i)'%(field.name)) 341 | print(' {') 342 | print(' if (%s && i > 0)'%(inline)) 343 | print(' s << ", ";') 344 | print(' else if (!%s)'%(inline)) 345 | print(' s << std::endl << indent << " -";') 346 | print(' Printer<%s>::stream(s, %s ? std::string() : indent + " ", v.%s[i]);'%(cpp_type, inline, field.name)) 347 | print(' }') 348 | print(' if (v.%s.empty() || %s)'%(field.name, inline)) 349 | print(' s << "]";') 350 | else: 351 | print(' Printer<%s>::stream(s, indent + " ", v.%s);'%(cpp_type, field.name)) 352 | }@ 353 | } 354 | @[else]@ 355 | template static void stream(Stream&, const std::string&, const @(cpp_full_name_with_alloc)&) 356 | {} 357 | @[end if]@ 358 | }; 359 | @[if has_plugin]@ 360 | #endif 361 | @[end if]@ 362 | 363 | } // namespace message_operations 364 | } // namespace ros 365 | 366 | @# Free function extensions provided by plugin 367 | @[if has_plugin_after]@ 368 | #include <@(spec.package)/plugin/@(spec.short_name).after.h> 369 | 370 | @[end if]@ 371 | #endif // @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_H 372 | -------------------------------------------------------------------------------- /scripts/srv.h.template: -------------------------------------------------------------------------------- 1 | @############################################### 2 | @# 3 | @# ROS message source code generation for C++ 4 | @# 5 | @# EmPy template for generating .h files 6 | @# 7 | @############################################### 8 | @# Start of Template 9 | @# 10 | @# Context: 11 | @# - file_name_in (String) Source .srv file 12 | @# - spec (msggen.SrvSpec) Parsed specification of the .srv file 13 | @# - md5sum (String) MD5Sum of the .srv specification 14 | @############################################### 15 | // Generated by gencpp from file @(spec.package)/@(spec.short_name).msg 16 | // DO NOT EDIT! 17 | 18 | @{ 19 | cpp_namespace = '::'+spec.package+'::' # TODO handle nested namespace 20 | cpp_class = spec.short_name 21 | cpp_full_name = cpp_namespace+cpp_class 22 | }@ 23 | 24 | #ifndef @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_H 25 | #define @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_H 26 | 27 | #include 28 | 29 | @############################################### 30 | @# Generate Request and Response Messages 31 | @############################################### 32 | 33 | #include <@(spec.package)/@(spec.short_name)Request.h> 34 | #include <@(spec.package)/@(spec.short_name)Response.h> 35 | 36 | @############################################### 37 | @# Service Struct 38 | @############################################### 39 | 40 | namespace @(spec.package) 41 | { 42 | 43 | struct @(spec.short_name) 44 | { 45 | 46 | typedef @(spec.request.short_name) Request; 47 | typedef @(spec.response.short_name) Response; 48 | Request request; 49 | Response response; 50 | 51 | typedef Request RequestType; 52 | typedef Response ResponseType; 53 | 54 | }; // struct @(spec.short_name) 55 | } // namespace @(spec.package) 56 | 57 | @############################################### 58 | @# Service-Traits 59 | @############################################### 60 | 61 | namespace ros 62 | { 63 | namespace service_traits 64 | { 65 | 66 | @[for trait_class,trait_value in [['MD5Sum', md5sum], ['DataType', spec.full_name]] ] 67 | template<> 68 | struct @trait_class< @cpp_full_name > { 69 | static const char* value() 70 | { 71 | return "@(trait_value)"; 72 | } 73 | 74 | static const char* value(const @(cpp_full_name)&) { return value(); } 75 | }; 76 | @[end for] 77 | 78 | // service_traits::MD5Sum< @(cpp_full_name)Request> should match 79 | // service_traits::MD5Sum< @cpp_full_name > 80 | template<> 81 | struct MD5Sum< @(cpp_full_name)Request> 82 | { 83 | static const char* value() 84 | { 85 | return MD5Sum< @cpp_full_name >::value(); 86 | } 87 | static const char* value(const @(cpp_full_name)Request&) 88 | { 89 | return value(); 90 | } 91 | }; 92 | 93 | // service_traits::DataType< @(cpp_full_name)Request> should match 94 | // service_traits::DataType< @cpp_full_name > 95 | template<> 96 | struct DataType< @(cpp_full_name)Request> 97 | { 98 | static const char* value() 99 | { 100 | return DataType< @cpp_full_name >::value(); 101 | } 102 | static const char* value(const @(cpp_full_name)Request&) 103 | { 104 | return value(); 105 | } 106 | }; 107 | 108 | // service_traits::MD5Sum< @(cpp_full_name)Response> should match 109 | // service_traits::MD5Sum< @cpp_full_name > 110 | template<> 111 | struct MD5Sum< @(cpp_full_name)Response> 112 | { 113 | static const char* value() 114 | { 115 | return MD5Sum< @cpp_full_name >::value(); 116 | } 117 | static const char* value(const @(cpp_full_name)Response&) 118 | { 119 | return value(); 120 | } 121 | }; 122 | 123 | // service_traits::DataType< @(cpp_full_name)Response> should match 124 | // service_traits::DataType< @cpp_full_name > 125 | template<> 126 | struct DataType< @(cpp_full_name)Response> 127 | { 128 | static const char* value() 129 | { 130 | return DataType< @cpp_full_name >::value(); 131 | } 132 | static const char* value(const @(cpp_full_name)Response&) 133 | { 134 | return value(); 135 | } 136 | }; 137 | 138 | } // namespace service_traits 139 | } // namespace ros 140 | 141 | #endif // @(spec.package.upper())_MESSAGE_@(spec.short_name.upper())_H 142 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from catkin_pkg.python_setup import generate_distutils_setup 2 | 3 | from setuptools import setup 4 | 5 | d = generate_distutils_setup( 6 | packages=['gencpp'], 7 | package_dir={'': 'src'}, 8 | requires=['genmsg'] 9 | ) 10 | 11 | setup(**d) 12 | -------------------------------------------------------------------------------- /src/gencpp/__init__.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2011, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import genmsg.msgs 34 | 35 | try: 36 | from cStringIO import StringIO # Python 2.x 37 | except ImportError: 38 | from io import StringIO # Python 3.x 39 | 40 | MSG_TYPE_TO_CPP = { 41 | 'byte': 'int8_t', 42 | 'char': 'uint8_t', 43 | 'bool': 'uint8_t', 44 | 'uint8': 'uint8_t', 45 | 'int8': 'int8_t', 46 | 'uint16': 'uint16_t', 47 | 'int16': 'int16_t', 48 | 'uint32': 'uint32_t', 49 | 'int32': 'int32_t', 50 | 'uint64': 'uint64_t', 51 | 'int64': 'int64_t', 52 | 'float32': 'float', 53 | 'float64': 'double', 54 | 'string': 'std::basic_string, typename std::allocator_traits::template rebind_alloc>', 55 | 'time': 'ros::Time', 56 | 'duration': 'ros::Duration', 57 | } 58 | 59 | 60 | # used 61 | def msg_type_to_cpp(type_): 62 | """ 63 | Convert a message type into the C++ declaration for that type. 64 | 65 | Example message types: uint32, std_msgs/String. 66 | Example C++ types: uint32_t, std_msgs::String_ 67 | 68 | @param type_: The message type 69 | @type type_: str 70 | @return: The C++ declaration 71 | @rtype: str 72 | """ 73 | (base_type, is_array, array_len) = genmsg.msgs.parse_type(type_) 74 | cpp_type = None 75 | if (genmsg.msgs.is_builtin(base_type)): 76 | cpp_type = MSG_TYPE_TO_CPP[base_type] 77 | elif (len(base_type.split('/')) == 1): 78 | if (genmsg.msgs.is_header_type(base_type)): 79 | cpp_type = ' ::std_msgs::Header_ ' 80 | else: 81 | cpp_type = '%s_ ' % (base_type) 82 | else: 83 | pkg = base_type.split('/')[0] 84 | msg = base_type.split('/')[1] 85 | cpp_type = ' ::%s::%s_ ' % (pkg, msg) 86 | 87 | if (is_array): 88 | if (array_len is None): 89 | return 'std::vector<%s, typename std::allocator_traits::template rebind_alloc<%s>>' % (cpp_type, cpp_type) 90 | else: 91 | return 'boost::array<%s, %s> ' % (cpp_type, array_len) 92 | else: 93 | return cpp_type 94 | 95 | 96 | def _escape_string(s): 97 | s = s.replace('\\', '\\\\') 98 | s = s.replace('"', '\\"') 99 | return s 100 | 101 | 102 | def escape_message_definition(definition): 103 | lines = definition.splitlines() 104 | if not lines: 105 | lines.append('') 106 | s = StringIO() 107 | for line in lines: 108 | line = _escape_string(line) 109 | # individual string literals cannot be too long; need to utilize string concatenation for long strings 110 | # https://docs.microsoft.com/en-us/cpp/c-language/maximum-string-length?view=vs-2017 111 | s.write('"%s\\n"\n' % (line)) 112 | 113 | val = s.getvalue() 114 | s.close() 115 | return val 116 | 117 | 118 | # used2 119 | def cpp_message_declarations(name_prefix, msg): 120 | """ 121 | Return the different possible C++ declarations for a message given the message itself. 122 | 123 | @param name_prefix: The C++ prefix to be prepended to the name, e.g. "std_msgs::" 124 | @type name_prefix: str 125 | @param msg: The message type 126 | @type msg: str 127 | @return: A tuple of 3 different names. cpp_message_decelarations("std_msgs::", "String") returns the tuple 128 | ("std_msgs::String_", "std_msgs::String_", "std_msgs::String") 129 | @rtype: str 130 | """ 131 | pkg, basetype = genmsg.names.package_resource_name(msg) 132 | cpp_name = ' ::%s%s' % (name_prefix, msg) 133 | if (pkg): 134 | cpp_name = ' ::%s::%s' % (pkg, basetype) 135 | return ('%s_' % (cpp_name), '%s_ ' % (cpp_name), '%s' % (cpp_name)) 136 | 137 | 138 | # todo 139 | def is_fixed_length(spec, msg_context, includepath): 140 | """ 141 | Return whether or not the message is fixed-length. 142 | 143 | @param spec: The message spec 144 | @type spec: genmsg.msgs.MsgSpec 145 | @param package: The package of the 146 | @type package: str 147 | """ 148 | types = [] 149 | for field in spec.parsed_fields(): 150 | if (field.is_array and field.array_len is None): 151 | return False 152 | 153 | if (field.base_type == 'string'): 154 | return False 155 | 156 | if (not field.is_builtin): 157 | types.append(field.base_type) 158 | 159 | types = set(types) 160 | for t in types: 161 | t = genmsg.msgs.resolve_type(t, spec.package) 162 | assert isinstance(includepath, dict) 163 | new_spec = genmsg.msg_loader.load_msg_by_type(msg_context, t, includepath) 164 | if (not is_fixed_length(new_spec, msg_context, includepath)): 165 | return False 166 | 167 | return True 168 | 169 | 170 | # used2 171 | def default_value(type_): 172 | """ 173 | Return the value to initialize a message member with. 174 | 175 | 0 for integer types, 0.0 for floating point, false for bool, 176 | empty string for everything else 177 | 178 | @param type_: The type 179 | @type type_: str 180 | """ 181 | if type_ in [ 182 | 'byte', 'int8', 'int16', 'int32', 'int64', 183 | 'char', 'uint8', 'uint16', 'uint32', 'uint64', 184 | ]: 185 | return '0' 186 | elif type_ in ['float32', 'float64']: 187 | return '0.0' 188 | elif type_ == 'bool': 189 | return 'false' 190 | 191 | return '' 192 | 193 | 194 | # used2 195 | def takes_allocator(type_): 196 | """ 197 | Return whether or not a type can take an allocator in its constructor. 198 | 199 | False for all builtin types except string. 200 | True for all others. 201 | 202 | @param type_: The type 203 | @type: str 204 | """ 205 | return type_ not in [ 206 | 'byte', 'int8', 'int16', 'int32', 'int64', 207 | 'char', 'uint8', 'uint16', 'uint32', 'uint64', 208 | 'float32', 'float64', 'bool', 'time', 'duration'] 209 | 210 | 211 | def escape_string(str_): 212 | str_ = str_.replace('\\', '\\\\') 213 | str_ = str_.replace('"', '\\"') 214 | return str_ 215 | 216 | 217 | # used 218 | def generate_fixed_length_assigns(spec, container_gets_allocator, cpp_name_prefix): 219 | """ 220 | Initialize any fixed-length arrays. 221 | 222 | @param s: The stream to write to 223 | @type s: stream 224 | @param spec: The message spec 225 | @type spec: genmsg.msgs.MsgSpec 226 | @param container_gets_allocator: Whether or not a container type (whether it's another message, a vector, array or string) 227 | should have the allocator passed to its constructor. Assumes the allocator is named _alloc. 228 | @type container_gets_allocator: bool 229 | @param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::" 230 | @type cpp_name_prefix: str 231 | """ 232 | # Assign all fixed-length arrays their default values 233 | for field in spec.parsed_fields(): 234 | if (not field.is_array or field.array_len is None): 235 | continue 236 | 237 | val = default_value(field.base_type) 238 | if (container_gets_allocator and takes_allocator(field.base_type)): 239 | # String is a special case, as it is the only builtin type that takes an allocator 240 | if (field.base_type == 'string'): 241 | string_cpp = msg_type_to_cpp('string') 242 | yield ' %s.assign(%s(_alloc));\n' % (field.name, string_cpp) 243 | else: 244 | (cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, field.base_type) 245 | yield ' %s.assign(%s(_alloc));\n' % (field.name, cpp_msg_with_alloc) 246 | elif (len(val) > 0): 247 | yield ' %s.assign(%s);\n' % (field.name, val) 248 | 249 | 250 | # used 251 | def generate_initializer_list(spec, container_gets_allocator): 252 | """ 253 | Write the initializer list for a constructor. 254 | 255 | @param s: The stream to write to 256 | @type s: stream 257 | @param spec: The message spec 258 | @type spec: genmsg.msgs.MsgSpec 259 | @param container_gets_allocator: Whether or not a container type (whether it's another message, a vector, array or string) 260 | should have the allocator passed to its constructor. Assumes the allocator is named _alloc. 261 | @type container_gets_allocator: bool 262 | """ 263 | op = ':' 264 | for field in spec.parsed_fields(): 265 | val = default_value(field.base_type) 266 | use_alloc = takes_allocator(field.base_type) 267 | if (field.is_array): 268 | if (field.array_len is None and container_gets_allocator): 269 | yield ' %s %s(_alloc)' % (op, field.name) 270 | else: 271 | yield ' %s %s()' % (op, field.name) 272 | else: 273 | if (container_gets_allocator and use_alloc): 274 | yield ' %s %s(_alloc)' % (op, field.name) 275 | else: 276 | yield ' %s %s(%s)' % (op, field.name, val) 277 | op = ',' 278 | --------------------------------------------------------------------------------