ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question

Revision history [back]

click to hide/show revision 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.

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.

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.

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.