Robotics StackExchange | Archived questions

Trouble Reading Back Array of Characters

In order to accommodate an encoded string that might have a terminal character, I'm trying to pass the data as a custom message with a char array rather than a standard string. Everything in the publisher seems to be fine, but when I try to read the array back using an iterator the error seems to tell me that the array entry is no longer the std::vector<char> that I expected, but now a std::vector<unsigned char, std::allocator<unsigned char> >::iterator that I can't assign to the iterator type I want to use.

This is my custom message, EncodedString.msg:

char[] data
int32 length

and this is the subscriber code where I try to read it back out:

#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/encoded_string.hpp"

class MinimalSubscriber : public rclcpp::Node
{
public:
  MinimalSubscriber()
  : Node("minimal_subscriber")
  {
    subscription_ = this->create_subscription<tutorial_interfaces::msg::EncodedString>(
      "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1));
  }

private:
  void topic_callback(const tutorial_interfaces::msg::EncodedString::SharedPtr msg) const
  {
    int char_length = msg->length;

    std::string encoded_bytes = "";
    std::vector<char>::iterator char_it;
    for( char_it = msg->data.begin(); msg->data.end(); char_it++ )
    {
      encoded_bytes.append( msg->data[char_it] );
    }
  }
  rclcpp::Subscription<tutorial_interfaces::msg::EncodedString>::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalSubscriber>());
  rclcpp::shutdown();
  return 0;
}

If it looks familiar, it's because I'm using the standard publisher/subscriber to test the concept before I do anything more complicated, so hopefully this is a proper minimal example. The problem arises when I go to build the package, and the first error that I get is:

Starting >>> cpp_pubsub
--- stderr: cpp_pubsub                             
/home/teddybouch/workspace/ros2_ws/src/cpp_pubsub/src/subscriber_member_function.cpp: In member function ‘void MinimalSubscriber::topic_callback(tutorial_interfaces::msg::EncodedString_<std::allocator<void> >::SharedPtr) const’:
/home/teddybouch/workspace/ros2_ws/src/cpp_pubsub/src/subscriber_member_function.cpp:45:36: error: no match for ‘operator=’ (operand types are ‘std::vector<char>::iterator’ {aka ‘__gnu_cxx::__normal_iterator<char*, std::vector<char> >’} and ‘std::vector<unsigned char, std::allocator<unsigned char> >::iterator’ {aka ‘__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >’})
   45 |     for( char_it = msg->data.begin(); msg->data.end(); char_it++ )
      |                                    ^
In file included from /usr/include/c++/9/bits/stl_algobase.h:67,
                 from /usr/include/c++/9/memory:62,
                 from /home/teddybouch/workspace/ros2_ws/src/cpp_pubsub/src/subscriber_member_function.cpp:15:
/usr/include/c++/9/bits/stl_iterator.h:784:11: note: candidate: ‘constexpr __gnu_cxx::__normal_iterator<char*, std::vector<char> >& __gnu_cxx::__normal_iterator<char*, std::vector<char> >::operator=(const __gnu_cxx::__normal_iterator<char*, std::vector<char> >&)’
  784 |     class __normal_iterator
      |           ^~~~~~~~~~~~~~~~~
/usr/include/c++/9/bits/stl_iterator.h:784:11: note:   no known conversion for argument 1 from ‘std::vector<unsigned char, std::allocator<unsigned char> >::iterator’ {aka ‘__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >’} to ‘const __gnu_cxx::__normal_iterator<char*, std::vector<char> >&’
/usr/include/c++/9/bits/stl_iterator.h:784:11: note: candidate: ‘constexpr __gnu_cxx::__normal_iterator<char*, std::vector<char> >& __gnu_cxx::__normal_iterator<char*, std::vector<char> >::operator=(__gnu_cxx::__normal_iterator<char*, std::vector<char> >&&)’
/usr/include/c++/9/bits/stl_iterator.h:784:11: note:   no known conversion for argument 1 from ‘std::vector<unsigned char, std::allocator<unsigned char> >::iterator’ {aka ‘__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >’} to ‘__gnu_cxx::__normal_iterator<char*, std::vector<char> >&&’

Can someone please clarify what is going on here for me?

Asked by teddybouch on 2022-05-23 23:08:52 UTC

Comments

Out of curiosity: what are you publishing that "is a string, but might contain a terminal character"?

Is it a binary payload? Would a plain unbounded uint8 array not be a better fit in that case?

Asked by gvdhoorn on 2022-05-24 02:03:44 UTC

I'm using DCCL to encode and decode protobufs, which outputs a string but in practice I've found will sometimes only pass part of the string when sent as a standard string message. I need to get it back into a string to decode on the other end, so using a char[] seemed more straightforward than dealing with the conversions to use a uint8[]. I may end up using the latter and can mess around with that option after work, but either way I think that it's worth my figuring out what's going on here that I can't get the char[] back on the subscriber end.

Asked by teddybouch on 2022-05-24 04:45:36 UTC

I'm using DCCL to encode and decode protobufs, which outputs a string [..]

an actual std::string, or a const char* or something similar?

In both cases sending that as a char[] field in a ROS message doesn't seem like it would be appropriate.

Having written that though: the error message basically tells you the iterator type is incompatible.

ROS messages use a configurable allocator, and the type of the iterator defined for the message type carries that type with it. You'll either have to match the type, or use auto.

Asked by gvdhoorn on 2022-05-24 06:53:31 UTC

Answers

According to http://design.ros2.org/articles/idl_interface_definition.html, by definition a char is unsigned in ros2.

In c++, a char is either signed or unsigned, whichever the compiler-writer decided to use. You (the developer) are responsible for handling the mismatch, if there is one.

Asked by Mike Scheutzow on 2022-05-24 06:38:32 UTC

Comments