<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=US-ASCII" />
    <title>Proposing std::join()</title>

    <style type="text/css">

    body { color: #000000; background-color: #FFFFFF; }
    del { text-decoration: line-through; color: #8B0040; }
    ins { text-decoration: underline; color: #005100; }

    p.example { margin-left: 2em; }
    pre.example { margin-left: 2em; }
    div.example { margin-left: 2em; }

    code.extract { background-color: #F5F6A2; }
    pre.extract { margin-left: 2em; background-color: #F5F6A2;
      border: 1px solid #E1E28E; }

    p.function { }
    .attribute { margin-left: 2em; }
    .attribute dt { float: left; font-style: italic;
      padding-right: 1ex; }
    .attribute dd { margin-left: 0em; }

    blockquote.std { color: #000000; background-color: #F1F1F1;
      border: 1px solid #D1D1D1;
      padding-left: 0.5em; padding-right: 0.5em; }
    blockquote.stddel { text-decoration: line-through;
      color: #000000; background-color: #FFEBFF;
      border: 1px solid #ECD7EC;
      padding-left: 0.5empadding-right: 0.5em; ; }

    blockquote.stdins { text-decoration: underline;
      color: #000000; background-color: #C8FFC8;
      border: 1px solid #B3EBB3; padding: 0.5em; }

    table { border: 1px solid black; border-spacing: 0px;
      margin-left: auto; margin-right: auto; }
    th { text-align: left; vertical-align: top;
      padding-left: 0.8em; border: none; }
    td { text-align: left; vertical-align: top;
      padding-left: 0.8em; border: none; }

    </style>

    <script
      src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"
      type="text/javascript"> </script>

    <script type="text/javascript">$(function() {
        var next_id = 0
        function find_id(node) {
            // Look down the first children of 'node' until we find one
            // with an id. If we don't find one, give 'node' an id and
            // return that.
            var cur = node[0];
            while (cur) {
                if (cur.id) return curid;
                if (cur.tagName == 'A' && cur.name)
                    return cur.name;
                cur = cur.firstChild;
            };
            // No id.
            node.attr('id', 'gensection-' + next_id++);
            return node.attr('id');
        };

        // Put a table of contents in the #toc nav.

        // This is a list of <ol> elements, where toc[N] is the list for
        // the current sequence of <h(N+2)> tags. When a header of an
        // existing level is encountered, all higher levels are popped,
        // and an <li> is appended to the level
        var toc = [$("<ol/>")];
        $(':header').not('h1').each(function() {
            var header = $(this);
            // For each <hN> tag, add a link to the toc at the appropriate
            // level.  When toc is one element too short, start a new list
            var levels = {H2: 0, H3: 1, H4: 2, H5: 3, H6: 4};
            var level = levels[this.tagName];
            if (typeof level == 'undefined') {
                throw 'Unexpected tag: ' + this.tagName;
            }
            // Truncate to the new level.
            toc.splice(level + 1, toc.length);
            if (toc.length < level) {
                // Omit TOC entries for skipped header levels.
                return;
            }
            if (toc.length == level) {
                // Add a <ol> to the previous level's last <li> and push
                // it into the array.
                var ol = $('<ol/>')
                toc[toc.length - 1].children().last().append(ol);
                toc.push(ol);
            }
            var header_text = header.text();
            toc[toc.length - 1].append(
                $('<li/>').append($('<a href="#' + find_id(header) + '"/>')
                                  .text(header_text)));
        });
        $('#toc').append(toc[0]);
    })
    </script>

  </head>
  <body>
    <h1><code>std::join()</code>: An algorithm for joining a range of elements</h1>

    <p>
    ISO/IEC JTC1 SC22 WG21 N3594 - 2013-03-13
    </p>

    <address>
      Greg Miller, jgm@google.com
    </address>

    <div id="toc">
    <!-- Generated dynamically by javascript -->
    </div>

    <h2><a name="introduction">Introduction</a></h2>

    <p> Creating a string by joining the elements in a collection with a
    separator between each element is a common task in many applications. Often
    the elements themselves are strings, so joining them is fairly straight
    forward. However, it is not uncommon to join collections of types other than
    string. For example, one might want to log a collection of <code>int</code>
    identifiers with each value separated by a comma and a space. In this case
    there needs to be some way to format the collection's element type as a
    string.</p>

    <p> The <code>std::join()</code> API described in this paper describes a
    function that is easy to use in all the common cases, such as joining
    strings and string-like objects as well as primitives like int, float, etc.
    This API is also extensible through a <em>Formatter</em> function object and
    is able to join arbitrary types. </p>

    <p> Joining strings is the inverse of splitting strings and they are often
    thought of together. This proposal describes a joining function to go along
    with the <code>std::split()</code> function described in N3593
    (<code>std::split</code>). </p>

    <p> This proposal depends on or refers to the following proposals: </p>
    <ul>
      <li>N3593 (<code>std::split</code>)</li>
      <li>N3609 (<code>std::string_view</code>)</li>
      <li>N3513 (<em>Range</em> support)</li>
    </ul>

    <h2><a name="synopsis">API Synopsis</a></h2>

    <h3><a name="std_join">std::join()</a></h3>

    <p> The function called to join a <em>Range</em> of elements with a
    separator into a single output string. </p>

    <pre class="example">
    <code>namespace std {

      // Range and Formatter
      template &lt;typename Range, typename Formatter&gt;
      std::string join(const Range&amp; range, std::string_view sep, Formatter f);

      // Range (a default formatter is used)
      template &lt;typename Range&gt;
      std::string join(const Range&amp; range, std::string_view sep);

    }</code>
    </pre>

    <p> There are two versions of <code>std::join()</code> that differ in that
    one of them takes an explicit <em>Formatter</em> function object and the
    other one uses a default Formatter. Both take a <em>Range</em> of <em>T</em>
    where <em>T</em> can be any object that is compatible with the <a
    href="#formatters"><em>Formatter</em></a> (whether the default Formatter or
    the one explicitly given). Both <code>std::join()</code> functions return
    the result as a <code>std::string</code>. </p>

    <dl>
      <dt><em>Requires:</em></dt>
      <dd>
      <em>Range</em> &mdash; a <em>Range</em> of elements that can be
      formatted using the specified (or default) <em>Formatter</em>. This
      argument should fully model the <em>Range</em> concept.
      </dd>
      <dd>
      <code>sep</code> &mdash; a separator string to be put between each
      formatted element in the output string.
      </dd>
      <dd>
      <em>Formatter</em> &mdash; a function object (or a lambda) that can format
      the <em>Range</em>'s element types and will append the formatted elements
      to the provided <code>std::string</code> reference. If no
      <em>Formatter</em> is explicitly given by the caller, a default will be
      used.
      </dd>
      <dt><em>Returns:</em></dt>
      <dd>
      A <code>std::string</code> containing the elements in the <em>Range</em>
      each separated by the given separator.
      </dd>
    </dl>

    <h3><a name="formatter_synopsis"><em>Formatter</em> template parameter</a></h3>

    <p> A <em>Formatter</em> is a function object that is responsible for
    formatting a type <em>T</em> and <strong>appending</strong> it the the given
    <code>std::string&amp;</code>. A <em>Formatter</em> must have a function
    call operator with the following signature. </p>

    <pre class="example">
    <code>void operator()(std::string&amp; output, T n)</code>;
    </pre>

    <p>Where the <em>Range</em>'s element type must be convertible to
    <code>T</code>.
    </p>

    <p> The following is an example formatter that can format any numeric type
    that is compatible with <code>std::to_string</code>. </p>

    <pre class="example">
    <code>
    struct number_formatter {
      template &lt;typename Number&gt;
      <strong>void operator()(std::string&amp; output, Number n) const</strong> {
      std::string s = std::to_string(n);
      output.append(s.data(), s.size());
      }
    };

    // Uses the number_formatter to join a vector of ints.
    vector&lt;int&gt; v1{1, 2, 3};
    std::string s1 = std::join(v1, "-", number_formatter());
    assert(s1 == "1-2-3");

    // Uses the number_formatter to join a vector of doubles.
    vector&lt;double&gt; v2{1.1, 2.2, 3.3};
    std::string s2 = std::join(v2, "-", number_formatter());
    assert(s2 == "1.1-2.2-3.3");
    </code>
    </pre>

    <h3><a name="default_formatter_synopsis">Default Formatter</a></h3>

    <p> When the two-argument form of <code>std::join()</code> is called no
    Formatter is explicitly given, so a default Formatter will be used. The
    default formatter will be able to format the following types: </p>
    <ul>
      <li><code>std::string</code></li>
      <li><code>std::string_view</code></li>
      <li><code>const char*</code></li>
      <li><code>char*</code></li>
      <li>All primitive types such as int, char, float, double, bool, etc.</li>
    </ul>

    <p>
    The above types should be formatted in the expected way; the same as would
    be done if the object was output with <code>std::to_string</code> or with
    <code>operator&lt;&lt;</code>.
    </p>

    <dl>
      <dt><em>Requires:</em></dt>
      <dd>
      Ability to format string-like objects and all primitive types.
      </dd>
      <dt><em>Returns:</em></dt>
      <dd>
      Nothing.
      </dd>
      <dt><em>Side-effects:</em></dt>
      <dd>
      The argument is formatted and appended to the given
      <code>std::string&amp;</code>.
      </dd>
    </dl>

    <h2><a name="api_usage">API Usage</a></h2>

    <ol>

    <li>
    <p> This example shows joining various types of containers of various
    element types. All of these examples will use the <em>default formatter</em>
    to convert the non-string-like elements into strings. </p>
    <pre class="example">
    <code>
    std::vector&lt;string&gt; vs{"foo", "bar", "baz"};
    std::string svs = std::join(vs, "-");
    assert(svs == "foo-bar-baz");

    std::vector&lt;const char*&gt; vc{"foo", "bar", "baz"};
    std::string svc = std::join(vc, "-");
    assert(svc == "foo-bar-baz");

    std::vector&lt;int&gt; vi{1, 2, 3};
    std::string svi = std::join(vi, "-");
    assert(svi == "1-2-3");

    double da[] = {1.1, 2.2, 3.3};
    std::string sda = std::join(da, "-");
    assert(sda == "1.1-2.2-3.3");
    </code>
    </pre>
    </li>

    <li>
    <p>This example shows the creation and use of a Formatter that knows how to
    format <code>std::pair&lt;const std::string, int&gt;</code> objects, with
    the first and second memebers of the pair separated by their own separator.
    This formatter could be used when joining a <code>std::map</code>.</p>
    <pre class="example">
    <code>
    class pair_formatter {
      std::string sep_;
     public:
      pair_formatter(std::string_view sep)
      : sep_(static_cast&lt;std::string&gt;(sep)))
      {}
      <strong>void operator()(std::string&amp; out, const std::pair&lt;const std::string, int&gt;&amp; p) const</strong> {
        out.append(p.first);
        out.append(sep_);
        out.append(std::to_string(p.second));
    };

    // Example use of the pair_formatter
    std::map&lt;std::string, int&gt; m = {
      std::make_pair("a", 1),
      std::make_pair("b", 2)
    };
    std::string s = std::join(m, ",", pair_formatter("="));
    assert(s == "a=1,b=2");  // Actual order may vary.
    </code>
    </pre>
    <p>
    The <code>pair_formatter</code> example above could be made a template to
    work with pairs of arbitrary types T and U.
    </p>
    </li>

    <li>
    <p> The following examples show a handful of edge cases to show how they
    will be handled. </p>
    <pre class="example">
    <code>
    // Joining an empty range
    std::vector&lt;std::string&gt; vempty;
    std::string sempty = std::join(vempty, "-");
    assert(sempty == "");

    // Joining a range of one element
    std::vector&lt;std::string&gt; vone{"foo"};
    std::string sone = std::join(vone, "-");
    assert(sone == "foo");

    // Joining a range of a single element that is the empty string
    std::vector&lt;std::string&gt; vone_empty{""};
    std::string sone_empty = std::join(vone_empty, "-");
    assert(sone_empty == "");

    // Joining a range of two elements with one being the empty string
    std::vector&lt;std::string&gt; vtwo{"foo", ""};
    std::string stwo = std::join(vtwo, "-");
    assert(stwo == "foo-");
    </code>
    </pre>

    </li>

    </ol>

    <h2><a name="open_questions">Open Questions</a></h2>

    <ul>
      <li>
      Should <code>std::join()</code> be able to return types other than
      <code>std::string</code>?
      </li>
      <li>
      Should a <em>Formatter</em> format its argument as a
      <code>std::string</code> and return it by value, or should it append the
      formatted value to a provided <code>std::string&amp;</code> as is
      described in this paper? This paper describes the appending solution for
      performance reasons.
      </li>
      <li>
      Should a <em>Formatter</em> append its output directly to a
      <code>std::string</code> or is there a better "appendable" interface that
      should be used?
      </li>

    </ul>

  </body>
</html>

