ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange |
1 | initial version |
There is sort of a simple rule to keep in mind when thinking about publish/subscribe custom class, you must always have a defined .msg
for each topic.
In you original question you mentioned that you have a message item_test.msg
defined as:
Item item
Where Item
is only otherwise defined as a C++ class. This will not work, you must have a corresponding .msg
definition of the Item
class. This will generate C++ and Python (others) code for the Item.msg
message definition, but in your code you can use your own Item
class and then use the message traits to have a custom serialization for your custom implementation of the Item
class.
For instance, you might define the Item.msg
file as:
string name
uint32 id
This will produce some C++ code like this (simplified):
namespace your_package_name {
struct Item_
{
std::string name;
uint32_t id;
};
typedef Item_ Item;
}
But then you could make your own Item
class:
namespace my_item_class {
class Item {
public:
void setName(std::string name) {name_ = name;}
void setId(size_t id) {id_ = id;}
private:
std::string name_;
size_t id;
};
}
Then you could write the necessary message traits to allow you to publish and subscribe to topics using your custom type (this is an "all-in-one" version):
namespace ros
{
namespace message_traits
{
template<> struct IsFixedSize<my_item_class::Item> : public TrueType {};
template<> struct IsSimple<my_item_class::Item> : public TrueType {};
template<>
struct MD5Sum<my_item_class::Item>
{
static const char* value()
{
return MD5Sum<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return MD5Sum<your_package_name::Item>::value(m);
}
};
template<>
struct DataType<my_item_class::Item>
{
static const char* value()
{
return DataType<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return DataType<your_package_name::Item>::value(m);
}
};
template<>
struct Definition<my_item_class::Item>
{
static const char* value()
{
return Definition<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return Definition<your_package_name::Item>::value(m);
}
};
} // namespace message_traits
namespace serialization
{
template<>
struct Serializer<my_item_class::Item>
{
template<typename Stream, typename T>
inline static void allInOne(Stream& stream, T item)
{
stream.next(item.getName());
uint32_t id = static_cast<uint32_t>(item.getId());
stream.next(id);
}
ROS_DECLARE_ALLINONE_SERIALIZER;
};
} // namespace serialization
} // namespace ros
Again the thing to remember is that in order to send something over a topic in ROS you need to have a fully defined message (.msg
file) with which to associate your custom class.
2 | No.2 Revision |
There is sort of a simple rule to keep in mind when thinking about publish/subscribe custom class, you must always have a defined .msg
for each topic.
In you original question you mentioned that you have a message item_test.msg
defined as:
Item item
Where Item
is only otherwise defined as a C++ class. This will not work, you must have a corresponding .msg
definition of the Item
class. This will generate C++ and Python (others) code for the Item.msg
message definition, but in your code you can use your own Item
class and then use the message traits to have a custom serialization for your custom implementation of the Item
class.
For instance, you might define the Item.msg
file as:
string name
uint32 id
This will produce some C++ code like this (simplified):
namespace your_package_name {
struct Item_
{
std::string name;
uint32_t id;
};
typedef Item_ Item;
}
But then you could make your own Item
class:
namespace my_item_class {
class Item {
public:
void setName(std::string name) {name_ = name;}
std::string getName() {return name_;}
void setId(size_t id) {id_ = id;}
size_t getId() {return id_;}
private:
std::string name_;
size_t id;
};
}
Then you could write the necessary message traits to allow you to publish and subscribe to topics using your custom type (this is an "all-in-one" version):
namespace ros
{
namespace message_traits
{
template<> struct IsFixedSize<my_item_class::Item> : public TrueType {};
template<> struct IsSimple<my_item_class::Item> : public TrueType {};
template<>
struct MD5Sum<my_item_class::Item>
{
static const char* value()
{
return MD5Sum<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return MD5Sum<your_package_name::Item>::value(m);
}
};
template<>
struct DataType<my_item_class::Item>
{
static const char* value()
{
return DataType<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return DataType<your_package_name::Item>::value(m);
}
};
template<>
struct Definition<my_item_class::Item>
{
static const char* value()
{
return Definition<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return Definition<your_package_name::Item>::value(m);
}
};
} // namespace message_traits
namespace serialization
{
template<>
struct Serializer<my_item_class::Item>
{
template<typename Stream, typename T>
inline static void allInOne(Stream& stream, T item)
{
stream.next(item.getName());
uint32_t id = static_cast<uint32_t>(item.getId());
stream.next(id);
}
ROS_DECLARE_ALLINONE_SERIALIZER;
};
} // namespace serialization
} // namespace ros
Again the thing to remember is that in order to send something over a topic in ROS you need to have a fully defined message (.msg
file) with which to associate your custom class.
3 | No.3 Revision |
There is sort of a simple rule to keep in mind when thinking about publish/subscribe custom class, you must always have a defined .msg
for each topic.
In you original question you mentioned that you have a message item_test.msg
defined as:
Item item
Where Item
is only otherwise defined as a C++ class. This will not work, you must have a corresponding .msg
definition of the Item
class. This will generate C++ and Python (others) code for the Item.msg
message definition, but in your code you can use your own Item
class and then use the message traits to have a custom serialization for your custom implementation of the Item
class.
For instance, you might define the Item.msg
file as:
string name
uint32 id
This will produce some C++ code like this (simplified):
namespace your_package_name {
struct Item_
{
std::string name;
uint32_t id;
};
typedef Item_ Item;
}
But then you could make your own Item
class:
namespace my_item_class {
class Item {
public:
void setName(std::string name) {name_ = name;}
std::string getName() {return name_;}
void setId(size_t id) {id_ = id;}
size_t getId() {return id_;}
private:
std::string name_;
size_t id;
};
}
Then you could write the necessary message traits to allow you to publish and subscribe to topics using your custom type (this is an "all-in-one" version):
namespace ros
{
namespace message_traits
{
template<> struct IsFixedSize<my_item_class::Item> : public TrueType {};
template<> struct IsSimple<my_item_class::Item> : public TrueType {};
template<>
struct MD5Sum<my_item_class::Item>
{
static const char* value()
{
return MD5Sum<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return MD5Sum<your_package_name::Item>::value(m);
}
};
template<>
struct DataType<my_item_class::Item>
{
static const char* value()
{
return DataType<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return DataType<your_package_name::Item>::value(m);
}
};
template<>
struct Definition<my_item_class::Item>
{
static const char* value()
{
return Definition<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return Definition<your_package_name::Item>::value(m);
}
};
} // namespace message_traits
namespace serialization
{
template<>
struct Serializer<my_item_class::Item>
{
template<typename Stream, typename T>
inline static void allInOne(Stream& stream, T item)
{
stream.next(item.getName());
uint32_t id = static_cast<uint32_t>(item.getId());
stream.next(id);
}
ROS_DECLARE_ALLINONE_SERIALIZER;
};
} // namespace serialization
} // namespace ros
The majority of the above code is just associating your custom class, my_item_class::Item
with the generated my_package_name::Item
message class. This association allows you to use the types interchangeably in your code, but tools and other nodes will always interpret it as a my_package_name::Item
unless they use the same message trait code above to receive it as the custom class too.
Again the thing to remember is that in order to send something over a topic in ROS you need to have a fully defined message (.msg
file) with which to associate your custom class.
4 | No.4 Revision |
There is sort of a simple rule to keep in mind when thinking about publish/subscribe custom class, you must always have a defined .msg
for each topic.
In you original question you mentioned that you have a message item_test.msg
defined as:
Item item
Where Item
is only otherwise defined as a C++ class. This will not work, you must have a corresponding .msg
definition of the Item
class. This will generate C++ and Python (others) code for the Item.msg
message definition, but in your code you can use your own Item
class and then use the message traits to have a custom serialization for your custom implementation of the Item
class.
For instance, you might define the Item.msg
file as:
string name
uint32 id
This will produce some C++ code like this (simplified):
namespace your_package_name {
struct Item_
{
std::string name;
uint32_t id;
};
typedef Item_ Item;
}
But then you could make your own Item
class:
namespace my_item_class {
class Item {
public:
void setName(std::string name) {name_ = name;}
std::string getName() {return name_;}
void setId(size_t id) {id_ = id;}
size_t getId() {return id_;}
private:
std::string name_;
size_t id;
};
}
Then you could write the necessary message traits to allow you to publish and subscribe to topics using your custom type (this is an "all-in-one" version):
namespace ros
{
namespace message_traits
{
template<> struct IsFixedSize<my_item_class::Item> : public TrueType {};
template<> struct IsSimple<my_item_class::Item> : public TrueType {};
template<>
struct MD5Sum<my_item_class::Item>
{
static const char* value()
{
return MD5Sum<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return MD5Sum<your_package_name::Item>::value(m);
}
};
template<>
struct DataType<my_item_class::Item>
{
static const char* value()
{
return DataType<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return DataType<your_package_name::Item>::value(m);
}
};
template<>
struct Definition<my_item_class::Item>
{
static const char* value()
{
return Definition<your_package_name::Item>::value();
}
static const char* value(const my_item_class::Item& m)
{
return Definition<your_package_name::Item>::value(m);
}
};
} // namespace message_traits
namespace serialization
{
template<>
struct Serializer<my_item_class::Item>
{
template<typename Stream, typename T>
inline static void allInOne(Stream& stream, T item)
{
stream.next(item.getName());
uint32_t id = static_cast<uint32_t>(item.getId());
stream.next(id);
}
ROS_DECLARE_ALLINONE_SERIALIZER;
};
} // namespace serialization
} // namespace ros
The majority of the above code is just associating your custom class, my_item_class::Item
, with the generated generated, my_package_name::Item
, message class. This association allows you to use the types interchangeably in your code, but tools and other nodes will always interpret it as a my_package_name::Item
unless they use the same message trait code above to receive it as the custom class too.
Again the thing to remember is that in order to send something over a topic in ROS you need to have a fully defined message (.msg
file) with which to associate your custom class.