A function for convenient string concatenation with variadic templates I wrote this function for string concatenation some time ago.

Jo() stands for "Join". The name is shortened on purpose, because this function is used very often.

It offers a stream-like functionality: Jo("i = ", i) = cout &lt;&lt; "i = " &lt;&lt; i
and returns a const char * to internal buffer.

const char * is used instead of std::string because I interact with C apis a lot and writing .c_str() each time is tedious.

I know that you can achieve same result by converting arguments to std::string one by one and concatenating them instead of using this function, but it would require a lot more code.

It's intended to be used in a single thread only, thus there are no mutextes or something similar.

Here is the code:

#include &lt;cstdio&gt;
#include &lt;sstream&gt;
#include &lt;initializer_list&gt;


namespace Internal
{
    // In real code those are declared `extern` and initialization is moved from .h into .cpp
    std::stringstream ss;
    const std::stringstream::fmtflags stdfmt = ss.flags();
}

// Jo() resets stringstream flags before performing concatenation.
// Jo_() does not.

template &lt;typename ...P&gt; const char *Jo_(P &amp;&amp;... p)
{
    // Multiple buffers to allow use of multiple Jo()'s in a single expression.
    static constexpr int ret_buffers_c = 32;
    static std::string ret_buffers[ret_buffers_c];
    static int ret_pos = 0;

    Internal::ss.clear(); // Clear the error flags.
    Internal::ss.str(""); // Clear stringstream contents.

    // Push all objects to stringstream.
    std::initializer_list&lt;int&gt;{(Internal::ss &lt;&lt; p, 0)...};

    // Save to buffer and return a pointer.
    ret_buffers[ret_pos] = Internal::ss.str();
    const char *ret = ret_buffers[ret_pos].c_str();
    ret_pos = (ret_pos + 1) % ret_buffers_c;
    return ret;
}

template &lt;typename ...P&gt; const char *Jo(P &amp;&amp;... p)
{
    Internal::ss.flags(Internal::stdfmt);
    return Jo_((P &amp;&amp;) p...);
}


// Example usage:
int main()
{
    for (int i = 1; i &lt;= 10; i++)
        std::puts(Jo(i, " * ", i+1, " = ", i*i+i));
    return 0;
}


Is there anything I can improve about those functions?

I'm especially concerned about this line: std::initializer_list&lt;int&gt;{(Internal::ss &lt;&lt; p, 0)...};
It looks a bit dirty, but I don't know any better alternative.
I don't like the idea in any way, but it is my personal opinion. The approach may bring up problems such as occasional buffer override if your buffer happens to be smaller than needed.

If you really don't want to write c_str() all the time you can wrap std::stringstream with some kind of proxy, that would have operator const char*() as a member function. The only problem is to get all the copies of member operator &lt;&lt; of the stringstream. I solved it using a template.

#include &lt;sstream&gt;
#include &lt;string&gt;

class sstream_proxy
{
    std::stringstream stream;
    std::string last;
public:
    template &lt;typename T&gt;
    sstream_proxy&amp; operator&lt;&lt;(const T&amp; value)
    {
        stream &lt;&lt; value;
        return *this;
    }

    operator const char*()
    {
        last = stream.str();
        stream.clear();
        stream.str("");
        return last.c_str();
    }
};


The buffer will have the same content unless you call converting operator again. If you want to have multiple buffers, you can have multiple instances of sstream_proxy. The code is not bulletproof, but much easier to read and understand. 

The code will be much more natural on its part.