Issues with serializing and deserializing C++ tensors (stringstream -> string - char* -> stringstream)

pregenRobot: <p>TLDR:</p>
<p>Using <code>torch::save</code> and <code>torch::load</code> on <code>std::stringstream</code> works just fine to save and load tensors. However, transforming the <code>std::stringstream</code> to a <code>std::string</code> and then a  <code>char*</code> to send over a TCP socket results in a broken message during deserialization. What could be the cause? Am I using it incorrectly?</p>
<p>Details:</p>
<p>I am working on a project that needs to transfer tensors over a tcp socket and need to serialize the tensors. Initially, I had code looking like the following to transfer a Tensor:</p>
<pre><code class="lang-auto">// CLIENT

  while (this-&gt;keep_alive.load()) {
    this_thread::sleep_for(chrono::seconds(5));
    auto tensor = torch::ones({3, 4});
    stringstream stream;
    torch::save(tensor, stream);
    
    string stream_str = stream.str();
    const char* stream_str_char_ptr = stream.c_str()
    if(::send(client_fd, stream_str_char_ptr, stream_str.size(), 0) &lt; 0){
      // Handle cases
    }
  }

// SERVER
  while (this-&gt;keep_alive.load()) {
    size_t read_so_far = 0;
    while (read_so_far &lt; this-&gt;read_size) {
      ssize_t valread =
          ::read(this-&gt;new_socket, this-&gt;read_buffer + read_so_far,
                 this-&gt;read_size - read_so_far);
      read_so_far += valread;
    };

    torch::Tensor input_tensor;
    stringstream buffer_stream(this-&gt;read_buffer);
    torch::load(input_tensor, buffer_stream);
    cout &lt;&lt; input_tensor &lt;&lt; endl;
  }
</code></pre>
<p>Now this code is raising the following error on the server side and I’m assuming this message means the serialization stream is corrupted:</p>
<pre><code class="lang-auto">libc++abi: terminating with uncaught exception of type c10::Error: istream reader failed: getting the current position.
Exception raised from validate at /Users/runner/work/pytorch/pytorch/pytorch/caffe2/serialize/istream_adapter.cc:32 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt;) + 81 (0x1070a4ca1 in libc10.dylib)
frame #1: c10::detail::torchCheckFail(char const*, char const*, unsigned int, std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt; const&amp;) + 98 (0x1070a3342 in libc10.dylib)
frame #2: caffe2::serialize::IStreamAdapter::validate(char const*) const + 124 (0x1231bd7bc in libtorch_cpu.dylib)
frame #3: caffe2::serialize::IStreamAdapter::size() const + 65 (0x1231bd6b1 in libtorch_cpu.dylib)
frame #4: caffe2::serialize::PyTorchStreamReader::init() + 99 (0x1231b81c3 in libtorch_cpu.dylib)
frame #5: caffe2::serialize::PyTorchStreamReader::PyTorchStreamReader(std::__1::basic_istream&lt;char, std::__1::char_traits&lt;char&gt; &gt;*) + 184 (0x1231b8b68 in libtorch_cpu.dylib)
frame #6: torch::jit::import_ir_module(std::__1::shared_ptr&lt;torch::jit::CompilationUnit&gt;, std::__1::basic_istream&lt;char, std::__1::char_traits&lt;char&gt; &gt;&amp;, c10::optional&lt;c10::Device&gt;, std::__1::unordered_map&lt;std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt;, std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt;, std::__1::hash&lt;std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt; &gt;, std::__1::equal_to&lt;std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt; &gt;, std::__1::allocator&lt;std::__1::pair&lt;std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt; const, std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt; &gt; &gt; &gt;&amp;, bool, bool) + 529 (0x12467e241 in libtorch_cpu.dylib)
frame #7: torch::jit::import_ir_module(std::__1::shared_ptr&lt;torch::jit::CompilationUnit&gt;, std::__1::basic_istream&lt;char, std::__1::char_traits&lt;char&gt; &gt;&amp;, c10::optional&lt;c10::Device&gt;, bool) + 75 (0x12467df6b in libtorch_cpu.dylib)
frame #8: torch::jit::load(std::__1::basic_istream&lt;char, std::__1::char_traits&lt;char&gt; &gt;&amp;, c10::optional&lt;c10::Device&gt;, bool) + 147 (0x124681c73 in libtorch_cpu.dylib)
frame #9: torch::serialize::InputArchive::load_from(std::__1::basic_istream&lt;char, std::__1::char_traits&lt;char&gt; &gt;&amp;, c10::optional&lt;c10::Device&gt;) + 28 (0x124ef814c in libtorch_cpu.dylib)
frame #10: void torch::load&lt;at::Tensor, std::__1::basic_stringstream&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt;&amp;&gt;(at::Tensor&amp;, std::__1::basic_stringstream&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt;&amp;) + 94 (0x106e3de0e in baton)
frame #11: baton::OutputClient::thread_handler() + 369 (0x106e42cb1 in baton)
frame #12: decltype(*(static_cast&lt;baton::OutputClient*&gt;(fp0)).*fp()) std::__1::__invoke&lt;void (baton::OutputClient::*)(), baton::OutputClient*, void&gt;(void (baton::OutputClient::*&amp;&amp;)(), baton::OutputClient*&amp;&amp;) + 105 (0x106e4d7f9 in baton)
frame #13: void std::__1::__thread_execute&lt;std::__1::unique_ptr&lt;std::__1::__thread_struct, std::__1::default_delete&lt;std::__1::__thread_struct&gt; &gt;, void (baton::OutputClient::*)(), baton::OutputClient*, 2ul&gt;(std::__1::tuple&lt;std::__1::unique_ptr&lt;std::__1::__thread_struct, std::__1::default_delete&lt;std::__1::__thread_struct&gt; &gt;, void (baton::OutputClient::*)(), baton::OutputClient*&gt;&amp;, std::__1::__tuple_indices&lt;2ul&gt;) + 62 (0x106e4d73e in baton)
frame #14: void* std::__1::__thread_proxy&lt;std::__1::tuple&lt;std::__1::unique_ptr&lt;std::__1::__thread_struct, std::__1::default_delete&lt;std::__1::__thread_struct&gt; &gt;, void (baton::OutputClient::*)(), baton::OutputClient*&gt; &gt;(void*) + 98 (0x106e4cf42 in baton)
frame #15: _pthread_start + 125 (0x7ff8149e14e1 in libsystem_pthread.dylib)
frame #16: thread_start + 15 (0x7ff8149dcf6b in libsystem_pthread.dylib)
</code></pre>
<p>So then I tried doing the serialization → string conversion → char* conversion all on the client side to make sure the issue was not caused by code on my side.</p>
<pre><code class="lang-auto">//CLIENT
  while (this-&gt;keep_alive.load()) {
    this_thread::sleep_for(chrono::seconds(5));
    auto tensor = torch::ones({3, 4});
    stringstream stream;
    torch::save(tensor, stream);

    torch::Tensor load_tensor;
    string stream_str = stream.str();
    const char *stream_str_char_ptr = stream_str.c_str();

    stringstream load_stream = stringstream(stream_str_char_ptr);
    torch::load(load_tensor, load_stream);
    cout &lt;&lt; load_tensor &lt;&lt; endl;
  }
</code></pre>
<p>However, this still raised the same error I pasted above. Suggesting the issue is with converting the <code>std::stringstream</code> to a <code>std::string</code> and then a <code>char *</code>. I will appreciate some advice on how to go around resolving this issue.</p>
<p>Edit:</p>
<p>The client and the server are both locally running on x86_64 MacOS.</p>

dfalbel: <aside class="quote no-group" data-username="pregenRobot" data-post="1" data-topic="183733">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/pregenrobot/48/29431_2.png" class="avatar"> pregenRobot:</div>
<blockquote>
<p><code>stringstream stream;</code></p>
</blockquote>
</aside>
<p>You might want to open the stream in binary mode as in:</p>
<pre><code class="lang-auto">std::ostringstream oss(std::ios::binary);
torch::save(t, oss);
</code></pre>
<p>I don’t remember the details anymore, but I had similar issues in the past related to null characters in the string that were fixed by opening the stream in binary mode.</p>