C++ Socket Part-2 A (Utilities)
I've only got a minor amount of suggestions to offer. Your code already does what it needs to do the proper way.
Missing headers
Consider including the specific header of the types you use instead of depending on other headers including them.

Requires &lt;cstddef&gt; for std::size_t.
Requires &lt;type_traits&gt; for std::enable_if&lt;&gt;
Requires &lt;tuple&gt; for std::tuple&lt;&gt; and std::get&lt;&gt;. This one  is actually needed, otherwise you get an undefined reference to std::get&lt;&gt;.

Printing
Consider...

Changing your condition to I == sizeof...( Tp ) - 1 instead of I == sizeof...( Tp ). Will the compiler optimize away the last extra empty function call? Probably, but there's no reason to have it to begin with.
Using the _t post-fix type traits for conciseness: std::enable_if_t&lt;&gt; over typename std::enable_if&lt;&gt;::type.
The default type alias of std::enable_if&lt;&gt; is already void, there's no need to explicitly write it.

By applying those suggestions, we go from this:
template&lt;std::size_t I = 0, typename... Tp&gt;
inline typename std::enable_if&lt;I == sizeof...(Tp), void&gt;::type
print(std::ostream&amp;, std::tuple&lt;Tp...&gt; const&amp;)
{ }

template&lt;std::size_t I = 0, typename... Tp&gt;
inline typename std::enable_if&lt;I &lt; sizeof...(Tp), void&gt;::type
print(std::ostream&amp; s, std::tuple&lt;Tp...&gt; const&amp; t)
{
    s &lt;&lt; std::get&lt;I&gt;(t);
    print&lt;I + 1, Tp...&gt;(s, t);
}

To this:
template&lt;std::size_t I = 0, typename... Tp&gt;
inline std::enable_if_t&lt;I == sizeof...( Tp ) - 1&gt;
print( std::ostream&amp; s, std::tuple&lt;Tp...&gt; const&amp; t )
{
    s &lt;&lt; std::get&lt;I&gt;( t );
}

template&lt;std::size_t I = 0, typename... Tp&gt;
inline std::enable_if_t&lt;I &lt; sizeof...( Tp ) - 1&gt;
print( std::ostream&amp; s, std::tuple&lt;Tp...&gt; const&amp; t )
{
    s &lt;&lt; std::get&lt;I&gt;( t );
    print&lt;I + 1, Tp...&gt;( s, t );
}


Using the array pack-expansion trick to avoid recursion, the use of std::tuple&lt;&gt; and indexing.

By using that trick, the print() function can be simplified into:
template &lt;class OutputStream, class... Args&gt;
void print( OutputStream&amp; os, Args&amp;&amp;... args )
{
    using expander = int[];
    expander{ ( (void) ( os &lt;&lt; std::forward&lt;Args&gt;( args ) ), 0 )... };
}

You can apply the same trick to the buildStringFromParts() function. This will allow you to separate printing from building the string.
To compensate for some standard warning flags that most people have turned on by default (-Werror -Wunused-value) we need to force a use of the temporary. So extract the first element as a return value:
template&lt;typename... Args&gt;
int print(std::ostream&amp; s, Args&amp;... args)
{
    using Expander = int[];
    return Expander{ 0, ((s &lt;&lt; std::forward&lt;Args&gt;(args)), 0)...}[0];
}