Sharing two strings of data between processes in C++

I’ve come into a problem recently where I had two separate processes that need to share two strings. (A dynamic IP address and a key) I’m used to using ROS for this, where I would define a ROS msg with the two strings and send it from one to the other.

However we are trying to go as simple as possible with our application here and thus avoid using third party SW as much as we can. To do so I originally planned to use shared memory to send a struct holding both std::string only to realize it is not a trivial problem as this struct’s size is dynamic…

I’ve also thought of using other means like sockets or queues, but I always run into the problem of not knowing beforehand the size of this struct. How can one deal with this problem? Is there a way to do so that doesn’t involve defining some protocol where you prepend the string with its size and end it with a null, or similar?

Here is a snip of my code that uses Qt to create a SharedMemory to pass this struct (unsuccessfully of course).

struct
{
   std::string ip_address;
   std::string key;
}server_data_t;
#define SERVER_DATA_LEN sizeof(server_data_t)


class SharedMemoryManager : public QSharedMemory
{
...
QByteArray SharedMemoryManager::read()
{
   QLOG_TRACE() << "SharedMemoryManager::read()";

   QByteArray readData;
   try
   {
      if(!isAttached())
         throw std::runtime_error("Share memory segment has not been opened yet");

      lock();
      readData = QByteArray(static_cast<char *>(data()), size());
      unlock();

      return readData;
   }
   catch (std::exception &e)
   {
      QLOG_ERROR() << e.what();

      return readData;
   }
}


void SharedMemoryManager::write(const QByteArray &byte_array)
{
   QLOG_TRACE() << "SharedMemoryManager::write()";

   try
   {
      if(!isAttached())
         throw std::runtime_error("Share memory segment has not been opened yet");

      lock();
      {
         auto *from = byte_array.data();
         char *to = static_cast<char*>(data());

         memcpy(to, from, qMin(size(), byte_array.size()));
      }
      unlock();
   }
   catch (std::exception &e)
   {
      QLOG_ERROR() << e.what();
   }
}

}

...

SharedMemoryManager _shm_manager;

server_data_t server_data;
server_data.ip_address = ...;
server_data.key = ...;

char *p = (char*)&server_data;

QByteArray byte_array = QByteArray(p, sizeof(server_data_t));

_shm_manager.write(byte_array);

...

SharedMemoryManager _shm_manager;
QByteArray byte_array = _shm_manager.read();

auto server_data = reinterpret_cast<server_data_t *>(byte_array.data());
std::cout << server_data->ip_address << std::endl;

When it tries to access to the string it fails.

Answer

Shared memory should be fine (you even let Qt do all the hard work) What you need is probably something like this, something that has a fixed size in your shared memory and still has enough space to hold your strings.

const std::size_t message_buf_size = 256;

struct data_t
{
   char message[message_buf_size]; // copy string into this and terminate with 0
   std::uint8_t ipv4[4];
}

Note that the data of the strings would have been allocated on the heap anyway (except for small strings they’re optimized) and not in shared memory.