Implement C++11 multipart sending without copying to a single buffer I am working on a C++11 project and am migrating the underlying communications library used.

The original library allowed sending messages in multiple parts using a SNDMORE flag:

socket_-&gt;send(str_1, SNDMORE);
socket_-&gt;send(str_2, SNDMORE); 
socket_-&gt;send(str_3); // The last message


and receiving them in multiple parts as well:

socket_-&gt;recv(&amp;msg);
... // do stuff
while(msg.more()) {
    socket_-&gt;recv(msg);
... // do stuff
}


The new library does not have this functionality, so I ended up creating a struct that merges upto 3 strings and then merges and sends, receives and splits them as required.

/**
*
* Created helper methods to merge 3 strings into 1 and reverse
* This is a helper to group 3 messages in a single string.
*
**/

struct MultipartSplitMerge {
   #define PADDING 8
   static std::string merge(std::string *s1, std::string *s2, std::string *s3) {
       std::string size_1 = std::to_string((*s1).size()); std::string size_2 = std::to_string((*s2).size()); 
       std::string padded_size_1 = std::string(PADDING - size_1.length(), '0') + size_1;
       std::string padded_size_2 = std::string(PADDING - size_2.length(), '0') + size_2;
       std::string ret_str(padded_size_1 + padded_size_2 + *s1 + *s2 + *s3);
       return ret_str; // Add the size of each piece
   }

   static void split(std::string *s, std::string *o1, std::string *o2, std::string *o3) {
       unsigned int size_1 = std::stoi((*s).substr (0, PADDING));
       unsigned int size_2 = std::stoi((*s).substr (PADDING, PADDING));
       *o1 = (*s).substr(2 * PADDING, size_1); 
       *o2 = (*s).substr(2 * PADDING + size_1, size_2); 
       *o3 = (*s).substr(2 * PADDING + size_1 + size_2);
   }
};


Using this splitter/merger, I now send as follows:

std::string nullstr(""); // I use this to leave one of the parts empty
socket_-&gt;send(MultipartSplitMerge::merge(&amp;h_array, &amp;msg, &amp;nullstr));


The receive code now works like this:

std::string combined, temp_1, temp_2, temp_3;
socket_-&gt;receive(&amp;combined);
MultipartSplitMerge::split(&amp;combined, &amp;temp_1, &amp;temp_2, &amp;temp_3); 


I'd like to avoid the unnecessary copies if possible, and also general guidelines on improving this code.

Here's the REPL to play with (or go to source):



&lt;script src="//repl.it/embed/MFW8/0.js"&gt;&lt;/script&gt;




Currently, we assume that there are 3 or less splits in every communication.
Use namespace if you want a namespace

You use your struct only as a way to have merge and split not in the global namespace (and inlined). That's not necessary. We can just use a namespace:

namespace MultipartSplitMerge {
    ...
}


Don't use define for compile-time constants

You define PADDING, but you never undefine it. It's a common word in code, so it's possible that you accidentally break something. Furthermore, it's not in the same namespace as merge and split, as the preprocessor does not care about namespaces.

Instead, use a static constexpr size_t:

static constexpr size_t PADDING = 8;


This has the nice side-effect that PADDING has now a type. The compiler can now warn you when you try to compare it to other, signed types.

Prefer references instead of pointers

Not only are they easier to use, they also are harder to get wrong.

Use const on call-by-reference/-pointer arguments that are not changed

You never change s1, s2 and s3, yet you didn't use const. We could have changed their values accidentally. A const will prevent this:

inline std::string merge(const std::string&amp; s1, const std::string&amp; s2, const std::string&amp; s3) {
    const std::string size_1 = std::to_string(s1.size()); 
    const std::string size_2 = std::to_string(s2.size()); 
    const std::string padded_size_1 = std::string(PADDING - size_1.length(), '0') + size_1;
    const std::string padded_size_2 = std::string(PADDING - size_2.length(), '0') + size_2;
    const std::string ret_str(padded_size_1 + padded_size_2 + s1 + s2 + s3);
    return ret_str; // Add the size of each piece
}

inline void split(const std::string&amp; s, std::string &amp;o1, std::string &amp;o2, std::string &amp;o3) {
    const unsigned int size_1 = std::stoi(s.substr(0, PADDING));
    const unsigned int size_2 = std::stoi(s.substr(PADDING, PADDING));
    o1 = s.substr(2 * PADDING, size_1); 
    o2 = s.substr(2 * PADDING + size_1, size_2); 
    o3 = s.substr(2 * PADDING + size_1 + size_2);
}


Note that all this inline annotations are only necessary if your functions will be in a header.

Don't repeat yourself

You pad your string, but it's a) hard to get and b) repetitive. Write a function to get rid of the duplicate logic:

inline std::string pad(size_t size, char padding, const std::string&amp; original) {
    assert(size &gt;= original.size());
    return std::string(size - original.size(), padding) + original;
}

inline std::string pad_size(const std::string&amp; original) {
    return pad(PADDING, '0', std::to_string(original.size()));
}


If you find an optimization for pad or pad_size, merge gets optimized for free. And it gets a lot shorter:

inline std::string merge(const std::string&amp; s1, const std::string&amp; s2, const std::string&amp; s3) {        
    return pad_size(s1) + pad_size(s2) + s1 + s2 + s3;
}


All code at once

Now let us inspect the reviewed and somewhat rewritten code:

namespace MultipartSplitMerge {
static constexpr size_t FIXED_FIELD_LENGTH = 8;

// Optional `detail' namespace; shows a user that `pad' and `pad_size'
// are implementation details. Only important in a header-only
// library
namespace detail {
inline std::string pad(size_t size, char padding, const std::string &amp;original) {
    assert(size &gt;= original.size());
    return std::string(size - original.size(), padding) + original;
}

inline std::string pad_size(const std::string &amp; original) {
    return pad(FIXED_FIELD_LENGTH, '0', std::to_string(original.size()));
}
} // namespace detail

inline std::string merge(const std::string&amp; s1, const std::string&amp; s2, const std::string&amp; s3) {        
    return detail::pad_size(s1) + detail::pad_size(s2) + s1 + s2 + s3;
}

inline void split(const std::string&amp; s, std::string&amp; o1, std::string&amp; o2, std::string&amp; o3) {
    const unsigned int size_1 = std::stoi(s.substr(0, FIXED_FIELD_LENGTH));
    const unsigned int size_2 = std::stoi(s.substr(FIXED_FIELD_LENGTH, FIXED_FIELD_LENGTH));
    o1 = s.substr(2 * FIXED_FIELD_LENGTH, size_1); 
    o2 = s.substr(2 * FIXED_FIELD_LENGTH + size_1, size_2); 
    o3 = s.substr(2 * FIXED_FIELD_LENGTH + size_1 + size_2);
}
} // namespace MultipartSplitMerge 


At this point I wouldn't change the code anymore, unless I see via profiling that split or merge is a bottle-neck.

If you really notice that merge is a bottle-neck, you can use sprintf, but I guess that the compiler should optimize above code.