<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2021-11-16" />
  <title>Formatting Ranges</title>
  <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
      ul.task-list{list-style: none;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {  background-color: #f6f8fa; }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span. { } /* Normal */
      code span.al { color: #ff0000; } /* Alert */
      code span.an { } /* Annotation */
      code span.at { } /* Attribute */
      code span.bn { color: #9f6807; } /* BaseN */
      code span.bu { color: #9f6807; } /* BuiltIn */
      code span.cf { color: #00607c; } /* ControlFlow */
      code span.ch { color: #9f6807; } /* Char */
      code span.cn { } /* Constant */
      code span.co { color: #008000; font-style: italic; } /* Comment */
      code span.cv { color: #008000; font-style: italic; } /* CommentVar */
      code span.do { color: #008000; } /* Documentation */
      code span.dt { color: #00607c; } /* DataType */
      code span.dv { color: #9f6807; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #9f6807; } /* Float */
      code span.fu { } /* Function */
      code span.im { } /* Import */
      code span.in { color: #008000; } /* Information */
      code span.kw { color: #00607c; } /* Keyword */
      code span.op { color: #af1915; } /* Operator */
      code span.ot { } /* Other */
      code span.pp { color: #6f4e37; } /* Preprocessor */
      code span.re { } /* RegionMarker */
      code span.sc { color: #9f6807; } /* SpecialChar */
      code span.ss { color: #9f6807; } /* SpecialString */
      code span.st { color: #9f6807; } /* String */
      code span.va { } /* Variable */
      code span.vs { color: #9f6807; } /* VerbatimString */
      code span.wa { color: #008000; font-weight: bold; } /* Warning */
      code.diff {color: #898887}
      code.diff span.va {color: #006e28}
      code.diff span.st {color: #bf0303}
  </style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
}
div.wrapper {
max-width: 60em;
margin: auto;
}
ul {
list-style-type: none;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
a {
text-decoration: none;
color: #4183C4;
}
a.hidden_link {
text-decoration: none;
color: inherit;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
h1, h2, h3, h4 {
position: relative;
line-height: 1;
}
a.self-link {
position: absolute;
top: 0;
left: calc(-1 * (3.5rem - 26px));
width: calc(3.5rem - 26px);
height: 2em;
text-align: center;
border: none;
transition: opacity .2s;
opacity: .5;
font-family: sans-serif;
font-weight: normal;
font-size: 83%;
}
a.self-link:hover { opacity: 1; }
a.self-link::before { content: "§"; }
ul > li:before {
content: "\2014";
position: absolute;
margin-left: -1.5em;
}
:target { background-color: #C9FBC9; }
:target .codeblock { background-color: #C9FBC9; }
:target ul { background-color: #C9FBC9; }
.abbr_ref { float: right; }
.folded_abbr_ref { float: right; }
:target .folded_abbr_ref { display: none; }
:target .unfolded_abbr_ref { float: right; display: inherit; }
.unfolded_abbr_ref { display: none; }
.secnum { display: inline-block; min-width: 35pt; }
.header-section-number { display: inline-block; min-width: 35pt; }
.annexnum { display: block; }
div.sourceLinkParent {
float: right;
}
a.sourceLink {
position: absolute;
opacity: 0;
margin-left: 10pt;
}
a.sourceLink:hover {
opacity: 1;
}
a.itemDeclLink {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
opacity: 0;
}
a.itemDeclLink:hover { opacity: 1; }
span.marginalizedparent {
position: relative;
left: -5em;
}
li span.marginalizedparent { left: -7em; }
li ul > li span.marginalizedparent { left: -9em; }
li ul > li ul > li span.marginalizedparent { left: -11em; }
li ul > li ul > li ul > li span.marginalizedparent { left: -13em; }
div.footnoteNumberParent {
position: relative;
left: -4.7em;
}
a.marginalized {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
}
a.enumerated_item_num {
position: relative;
left: -3.5em;
display: inline-block;
margin-right: -3em;
text-align: right;
width: 3em;
}
div.para { margin-bottom: 0.6em; margin-top: 0.6em; text-align: justify; }
div.section { text-align: justify; }
div.sentence { display: inline; }
span.indexparent {
display: inline;
position: relative;
float: right;
right: -1em;
}
a.index {
position: absolute;
display: none;
}
a.index:before { content: "⟵"; }

a.index:target {
display: inline;
}
.indexitems {
margin-left: 2em;
text-indent: -2em;
}
div.itemdescr {
margin-left: 3em;
}
.bnf {
font-family: serif;
margin-left: 40pt;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.ncbnf {
font-family: serif;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
}
.ncsimplebnf {
font-family: serif;
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
background: inherit; 
}
span.textnormal {
font-style: normal;
font-family: serif;
white-space: normal;
display: inline-block;
}
span.rlap {
display: inline-block;
width: 0px;
}
span.descr { font-style: normal; font-family: serif; }
span.grammarterm { font-style: italic; }
span.term { font-style: italic; }
span.terminal { font-family: monospace; font-style: normal; }
span.nonterminal { font-style: italic; }
span.tcode { font-family: monospace; font-style: normal; }
span.textbf { font-weight: bold; }
span.textsc { font-variant: small-caps; }
a.nontermdef { font-style: italic; font-family: serif; }
span.emph { font-style: italic; }
span.techterm { font-style: italic; }
span.mathit { font-style: italic; }
span.mathsf { font-family: sans-serif; }
span.mathrm { font-family: serif; font-style: normal; }
span.textrm { font-family: serif; }
span.textsl { font-style: italic; }
span.mathtt { font-family: monospace; font-style: normal; }
span.mbox { font-family: serif; font-style: normal; }
span.ungap { display: inline-block; width: 2pt; }
span.textit { font-style: italic; }
span.texttt { font-family: monospace; }
span.tcode_in_codeblock { font-family: monospace; font-style: normal; }
span.phantom { color: white; }

span.math { font-style: normal; }
span.mathblock {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1.2em;
margin-bottom: 1.2em;
text-align: center;
}
span.mathalpha {
font-style: italic;
}
span.synopsis {
font-weight: bold;
margin-top: 0.5em;
display: block;
}
span.definition {
font-weight: bold;
display: block;
}
.codeblock {
margin-left: 1.2em;
line-height: 127%;
}
.outputblock {
margin-left: 1.2em;
line-height: 127%;
}
div.itemdecl {
margin-top: 2ex;
}
code.itemdeclcode {
white-space: pre;
display: block;
}
span.textsuperscript {
vertical-align: super;
font-size: smaller;
line-height: 0;
}
.footnotenum { vertical-align: super; font-size: smaller; line-height: 0; }
.footnote {
font-size: small;
margin-left: 2em;
margin-right: 2em;
margin-top: 0.6em;
margin-bottom: 0.6em;
}
div.minipage {
display: inline-block;
margin-right: 3em;
}
div.numberedTable {
text-align: center;
margin: 2em;
}
div.figure {
text-align: center;
margin: 2em;
}
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
text-align: left;
hyphens: none; 
}
td, th {
padding-left: 1em;
padding-right: 1em;
vertical-align: top;
}
td.empty {
padding: 0px;
padding-left: 1px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
td.justify {
text-align: justify;
}
td.border {
border-left: 1px solid black;
}
tr.rowsep, td.cline {
border-top: 1px solid black;
}
tr.even, tr.odd {
border-bottom: 1px solid black;
}
tr.capsep {
border-top: 3px solid black;
border-top-style: double;
}
tr.header {
border-bottom: 3px solid black;
border-bottom-style: double;
}
th {
border-bottom: 1px solid black;
}
span.centry {
font-weight: bold;
}
div.table {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 90%;
}
span.indented {
display: block;
margin-left: 2em;
margin-bottom: 1em;
margin-top: 1em;
}
ol.enumeratea { list-style-type: none; background: inherit; }
ol.enumerate { list-style-type: none; background: inherit; }

code.sourceCode > span { display: inline; }
</style>
  <style type="text/css">a {
color : #4183C4;
text-decoration: underline;
}
a.marginalized {
text-decoration: none;
}
a.self-link {
text-decoration: none;
}
h1#toctitle {
border-bottom: 1px solid #cccccc;
}
#TOC li {
margin-top: 1px;
margin-bottom: 1px;
}
#TOC ul>li:before { display: none; }
h3.subtitle { margin-top: -15px; }
h1:target { background-color: transparent; }
h2:target { background-color: transparent; }
h3:target { background-color: transparent; }
h4:target { background-color: transparent; }
h5:target { background-color: transparent; }
h6:target { background-color: transparent; }
code span.co { font-family: monospace; }
table tr {
background-color: white;
}
table tr:nth-child(2n) {
background-color: #f6f8fa;
}
#title-block-header > table tr:nth-child(2n) {
background-color: white;
}
td > div.sourceCode {
background-color: inherit;
}
table {
border-collapse: collapse;
}
table td, table th {
border: 1px solid #cccccc;
}
table th {
border-bottom: 1px solid black;
text-align: center;
}
table tr:first-child th {
border-top: 0;
}
table tr:last-child td {
border-bottom: 0;
}
table tr td:first-child,
table tr th:first-child {
border-left: 0;
}
table tr td:last-child,
table tr th:last-child {
border-right: 0;
}
table tbody tr:first-child td {
border-top: 1px solid black;
}
#title-block-header td { border: 0; }
@media all {
body {
margin: 2em;
}
}
@media screen and (min-width: 480px) {
body {
margin: 5em;
}
}
#refs code{padding-left: 0px; text-indent: 0px;}
:root {
--diff-ins: #e6ffed;
--diff-strongins: #acf2bd;
--diff-del: #ffdddd;
--diff-strongdel: #ff8888;
}
span.diffins {
background-color: var(--diff-strongins);
}
span.diffdel {
background-color: var(--diff-strongdel);
}
div.rm { text-decoration: line-through; }
div.rm code.sourceCode { text-decoration: line-through; }
div.addu, span.addu {
color: #006e28;
background-color: var(--diff-ins);
}

div.rm pre, div.add pre { background-color: #f6f8fa; }
div.addu pre { background-color: var(--diff-ins); }
div.add, div.add pre { background-color: var(--diff-ins); }
div.addu blockquote {
border-left: 4px solid #00a000;
padding: 0 15px;
color: #006e28;
text-decoration: none;
}
div.addu blockquote code.sourceCode { text-decoration: none; }
div.addu blockquote pre { text-decoration: none; }
div.addu blockquote pre code { text-decoration: none; }
div.quote {
border-left: 7px solid #ccc;
background: #f9f9f9;
margin: 1.5em 10px;
padding-left: 20px;
}
code.diff span.va { color: #000000; background-color: var(--diff-ins); }
code.diff span.st { color: #000000; background-color: var(--diff-del); }
</style>
  <link href="data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
  
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Formatting Ranges</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P2286R3</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2021-11-16</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      LEWG<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Barry Revzin<br>&lt;<a href="mailto:barry.revzin@gmail.com" class="email">barry.revzin@gmail.com</a>&gt;<br>
    </td>
  </tr>
</table>

</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#revision-history"><span class="toc-section-number">1</span> Revision History<span></span></a></li>
<li><a href="#introduction"><span class="toc-section-number">2</span> Introduction<span></span></a>
<ul>
<li><a href="#implementation-experience"><span class="toc-section-number">2.1</span> Implementation Experience<span></span></a></li>
</ul></li>
<li><a href="#proposal-considerations"><span class="toc-section-number">3</span> Proposal Considerations<span></span></a>
<ul>
<li><a href="#what-types-to-print"><span class="toc-section-number">3.1</span> What types to print?<span></span></a></li>
<li><a href="#what-representation"><span class="toc-section-number">3.2</span> What representation?<span></span></a>
<ul>
<li><a href="#vector-and-other-ranges"><span class="toc-section-number">3.2.1</span> <code class="sourceCode cpp">vector</code> (and other ranges)<span></span></a></li>
<li><a href="#pair-and-tuple"><span class="toc-section-number">3.2.2</span> <code class="sourceCode cpp">pair</code> and <code class="sourceCode cpp">tuple</code><span></span></a></li>
<li><a href="#map-and-set-and-other-associative-containers"><span class="toc-section-number">3.2.3</span> <code class="sourceCode cpp">map</code> and <code class="sourceCode cpp">set</code> (and other associative containers)<span></span></a></li>
<li><a href="#char-and-string-and-other-string-like-types-in-ranges-or-tuples"><span class="toc-section-number">3.2.4</span> <code class="sourceCode cpp"><span class="dt">char</span></code> and <code class="sourceCode cpp">string</code> (and other string-like types) in ranges or tuples<span></span></a></li>
<li><a href="#format-specifiers"><span class="toc-section-number">3.2.5</span> Format Specifiers<span></span></a></li>
<li><a href="#explanation-of-added-specifiers"><span class="toc-section-number">3.2.6</span> Explanation of Added Specifiers<span></span></a></li>
</ul></li>
<li><a href="#implementation-challenges"><span class="toc-section-number">3.3</span> Implementation Challenges<span></span></a>
<ul>
<li><a href="#wrapping-basic_format_context-is-not-generally-possible"><span class="toc-section-number">3.3.1</span> Wrapping <code class="sourceCode cpp">basic_format_context</code> is not generally possible<span></span></a></li>
<li><a href="#manipulating-basic_format_parse_context-to-search-for-sentinels"><span class="toc-section-number">3.3.2</span> Manipulating <code class="sourceCode cpp">basic_format_parse_context</code> to search for sentinels<span></span></a></li>
<li><a href="#parsing-of-alignment-padding-and-width"><span class="toc-section-number">3.3.3</span> Parsing of alignment, padding, and width<span></span></a></li>
</ul></li>
<li><a href="#how-to-support-those-views-which-are-not-const-iterable"><span class="toc-section-number">3.4</span> How to support those views which are not <code class="sourceCode cpp"><span class="kw">const</span></code>-iterable?<span></span></a></li>
<li><a href="#what-additional-functionality"><span class="toc-section-number">3.5</span> What additional functionality?<span></span></a></li>
<li><a href="#format-or-stdcout"><span class="toc-section-number">3.6</span> <code class="sourceCode cpp">format</code> or <code class="sourceCode cpp">std<span class="op">::</span>cout</code>?<span></span></a></li>
<li><a href="#what-about-vectorbool"><span class="toc-section-number">3.7</span> What about <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span></code>?<span></span></a></li>
</ul></li>
<li><a href="#proposal"><span class="toc-section-number">4</span> Proposal<span></span></a>
<ul>
<li><a href="#wording"><span class="toc-section-number">4.1</span> Wording<span></span></a></li>
</ul></li>
<li><a href="#bibliography"><span class="toc-section-number">5</span> References<span></span></a></li>
</ul>
</div>
<h1 data-number="1" style="border-bottom:1px solid #cccccc" id="revision-history"><span class="header-section-number">1</span> Revision History<a href="#revision-history" class="self-link"></a></h1>
<p>Since <span class="citation" data-cites="P2286R2">[<a href="#ref-P2286R2" role="doc-biblioref">P2286R2</a>]</span>, several major changes:</p>
<ul>
<li>This paper assumes the adoption of <span class="citation" data-cites="P2418R0">[<a href="#ref-P2418R0" role="doc-biblioref">P2418R0</a>]</span>, which affects how <a href="#how-to-support-those-views-which-are-not-const-iterable">non-<code class="sourceCode cpp"><span class="kw">const</span></code>-iterable views</a> are handled. This paper now introduces two concepts (<code class="sourceCode cpp">formattable</code> and <code class="sourceCode cpp">const_formattable</code>) instead of just one.</li>
<li>Extended discussion and functionality for various <a href="#what-representation">representations</a>, including how to quote strings properly and how to format associative ranges.</li>
<li>Introduction of format specifiers of all kinds and discussion of how to make them work more broadly.</li>
<li>Removed the wording, since the priority is the design.</li>
</ul>
<p>Since <span class="citation" data-cites="P2286R1">[<a href="#ref-P2286R1" role="doc-biblioref">P2286R1</a>]</span>, adding a sketch of wording.</p>
<p><span class="citation" data-cites="P2286R0">[<a href="#ref-P2286R0" role="doc-biblioref">P2286R0</a>]</span> suggested making all the formatting implementation-defined. Several people reached out to me suggesting in no uncertain terms that this is unacceptable. This revision lays out options for such formatting.</p>
<h1 data-number="2" style="border-bottom:1px solid #cccccc" id="introduction"><span class="header-section-number">2</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p><span class="citation" data-cites="LWG3478">[<a href="#ref-LWG3478" role="doc-biblioref">LWG3478</a>]</span> addresses the issue of what happens when you split a string and the last character in the string is the delimiter that you are splitting on. One of the things I wanted to look at in research in that issue is: what do <em>other</em> languages do here?</p>
<p>For most languages, this is a pretty easy proposition. Do the split, print the results. This is usually only a few lines of code.</p>
<p>Python:</p>
<blockquote>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="bu">print</span>(<span class="st">&quot;xyx&quot;</span>.split(<span class="st">&quot;x&quot;</span>))</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1"></a>[&#39;&#39;, &#39;y&#39;, &#39;&#39;]</span></code></pre></div>
</blockquote>
<p>Java (where the obvious thing prints something useless, but there’s a non-obvious thing that is useful):</p>
<blockquote>
<div class="sourceCode" id="cb2"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">import</span><span class="im"> java.util.Arrays;</span></span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a><span class="kw">class</span> Main {</span>
<span id="cb2-4"><a href="#cb2-4"></a>  <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span>(<span class="bu">String</span> args[]) {</span>
<span id="cb2-5"><a href="#cb2-5"></a>    <span class="bu">System</span>.<span class="fu">out</span>.<span class="fu">println</span>(<span class="st">&quot;xyx&quot;</span>.<span class="fu">split</span>(<span class="st">&quot;x&quot;</span>));</span>
<span id="cb2-6"><a href="#cb2-6"></a>    <span class="bu">System</span>.<span class="fu">out</span>.<span class="fu">println</span>(<span class="bu">Arrays</span>.<span class="fu">toString</span>(<span class="st">&quot;xyx&quot;</span>.<span class="fu">split</span>(<span class="st">&quot;x&quot;</span>)));</span>
<span id="cb2-7"><a href="#cb2-7"></a>  }</span>
<span id="cb2-8"><a href="#cb2-8"></a>}</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb2-1"><a href="#cb2-1"></a>[Ljava.lang.String;@76ed5528</span>
<span id="cb2-2"><a href="#cb2-2"></a>[, y]</span></code></pre></div>
</blockquote>
<p>Rust (a couple options, including also another false friend):</p>
<blockquote>
<div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">use</span> <span class="pp">itertools::</span>Itertools<span class="op">;</span></span>
<span id="cb3-2"><a href="#cb3-2"></a></span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="kw">fn</span> main() <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4"></a>    <span class="pp">println!</span>(<span class="st">&quot;{:?}&quot;</span><span class="op">,</span> <span class="st">&quot;xyx&quot;</span><span class="op">.</span>split(<span class="ch">&#39;x&#39;</span>))<span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5"></a>    <span class="pp">println!</span>(<span class="st">&quot;[{}]&quot;</span><span class="op">,</span> <span class="st">&quot;xyx&quot;</span><span class="op">.</span>split(<span class="ch">&#39;x&#39;</span>)<span class="op">.</span>format(<span class="st">&quot;, &quot;</span>))<span class="op">;</span></span>
<span id="cb3-6"><a href="#cb3-6"></a>    <span class="pp">println!</span>(<span class="st">&quot;{:?}&quot;</span><span class="op">,</span> <span class="st">&quot;xyx&quot;</span><span class="op">.</span>split(<span class="ch">&#39;x&#39;</span>)<span class="op">.</span><span class="pp">collect::</span><span class="op">&lt;</span><span class="dt">Vec</span><span class="op">&lt;</span>_<span class="op">&gt;&gt;</span>())<span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="op">}</span></span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb3-1"><a href="#cb3-1"></a>Split(SplitInternal { start: 0, end: 3, matcher: CharSearcher { haystack: &quot;xyx&quot;, finger: 0, finger_back: 3, needle: &#39;x&#39;, utf8_size: 1, utf8_encoded: [120, 0, 0, 0] }, allow_trailing_empty: true, finished: false })</span>
<span id="cb3-2"><a href="#cb3-2"></a>[, y, ]</span>
<span id="cb3-3"><a href="#cb3-3"></a>[&quot;&quot;, &quot;y&quot;, &quot;&quot;]</span></code></pre></div>
</blockquote>
<p>Kotlin:</p>
<blockquote>
<div class="sourceCode" id="cb4"><pre class="sourceCode kotlin"><code class="sourceCode kotlin"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">fun</span> <span class="fu">main</span>() {</span>
<span id="cb4-2"><a href="#cb4-2"></a>    println(<span class="st">&quot;xyx&quot;</span>.split(<span class="st">&quot;x&quot;</span>));</span>
<span id="cb4-3"><a href="#cb4-3"></a>}</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb4-1"><a href="#cb4-1"></a>[, y, ]</span></code></pre></div>
</blockquote>
<p>Go:</p>
<blockquote>
<div class="sourceCode" id="cb5"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">package</span> main</span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw">import</span> <span class="st">&quot;fmt&quot;</span></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="kw">import</span> <span class="st">&quot;strings&quot;</span></span>
<span id="cb5-4"><a href="#cb5-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="kw">func</span> main() {</span>
<span id="cb5-6"><a href="#cb5-6"></a>    fmt.Println(strings.Split(<span class="st">&quot;xyx&quot;</span>, <span class="st">&quot;x&quot;</span>));</span>
<span id="cb5-7"><a href="#cb5-7"></a>}</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb5-1"><a href="#cb5-1"></a>[ y ]</span></code></pre></div>
</blockquote>
<p>JavaScript:</p>
<blockquote>
<div class="sourceCode" id="cb6"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1"></a><span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">&#39;xyx&#39;</span><span class="op">.</span><span class="fu">split</span>(<span class="st">&#39;x&#39;</span>))</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb6-1"><a href="#cb6-1"></a>[ &#39;&#39;, &#39;y&#39;, &#39;&#39; ]</span></code></pre></div>
</blockquote>
<p>And so on and so forth. What we see across these languages is that printing the result of split is pretty easy. In most cases, whatever the print mechanism is just works and does something meaningful. In other cases, printing gave me something other than what I wanted but some other easy, provided mechanism for doing so.</p>
<p>Now let’s consider C++.</p>
<blockquote>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1"></a><span class="pp">#include </span><span class="im">&lt;iostream&gt;</span></span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="pp">#include </span><span class="im">&lt;string&gt;</span></span>
<span id="cb7-3"><a href="#cb7-3"></a><span class="pp">#include </span><span class="im">&lt;ranges&gt;</span></span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="pp">#include </span><span class="im">&lt;format&gt;</span></span>
<span id="cb7-5"><a href="#cb7-5"></a></span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb7-7"><a href="#cb7-7"></a>    <span class="co">// need to predeclare this because we can&#39;t split an rvalue string</span></span>
<span id="cb7-8"><a href="#cb7-8"></a>    std<span class="op">::</span>string s <span class="op">=</span> <span class="st">&quot;xyx&quot;</span>;</span>
<span id="cb7-9"><a href="#cb7-9"></a>    <span class="kw">auto</span> parts <span class="op">=</span> s <span class="op">|</span> std<span class="op">::</span>views<span class="op">::</span>split<span class="op">(</span><span class="ch">&#39;x&#39;</span><span class="op">)</span>;</span>
<span id="cb7-10"><a href="#cb7-10"></a></span>
<span id="cb7-11"><a href="#cb7-11"></a>    <span class="co">// nope</span></span>
<span id="cb7-12"><a href="#cb7-12"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> parts;</span>
<span id="cb7-13"><a href="#cb7-13"></a></span>
<span id="cb7-14"><a href="#cb7-14"></a>    <span class="co">// nope (assuming std::print from P2093)</span></span>
<span id="cb7-15"><a href="#cb7-15"></a>    std<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{}&quot;</span>, parts<span class="op">)</span>;</span>
<span id="cb7-16"><a href="#cb7-16"></a></span>
<span id="cb7-17"><a href="#cb7-17"></a></span>
<span id="cb7-18"><a href="#cb7-18"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;[&quot;</span>;</span>
<span id="cb7-19"><a href="#cb7-19"></a>    <span class="dt">char</span> <span class="kw">const</span><span class="op">*</span> delim <span class="op">=</span> <span class="st">&quot;&quot;</span>;</span>
<span id="cb7-20"><a href="#cb7-20"></a>    <span class="cf">for</span> <span class="op">(</span><span class="kw">auto</span> part <span class="op">:</span> parts<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-21"><a href="#cb7-21"></a>        std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> delim;</span>
<span id="cb7-22"><a href="#cb7-22"></a></span>
<span id="cb7-23"><a href="#cb7-23"></a>        <span class="co">// still nope</span></span>
<span id="cb7-24"><a href="#cb7-24"></a>        std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> part;</span>
<span id="cb7-25"><a href="#cb7-25"></a></span>
<span id="cb7-26"><a href="#cb7-26"></a>        <span class="co">// also nope</span></span>
<span id="cb7-27"><a href="#cb7-27"></a>        std<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{}&quot;</span>, part<span class="op">)</span>;</span>
<span id="cb7-28"><a href="#cb7-28"></a></span>
<span id="cb7-29"><a href="#cb7-29"></a>        <span class="co">// this finally works</span></span>
<span id="cb7-30"><a href="#cb7-30"></a>        std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>part, std<span class="op">::</span>ostream_iterator<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;(</span>std<span class="op">::</span>cout<span class="op">))</span>;</span>
<span id="cb7-31"><a href="#cb7-31"></a></span>
<span id="cb7-32"><a href="#cb7-32"></a>        <span class="co">// as does this</span></span>
<span id="cb7-33"><a href="#cb7-33"></a>        <span class="cf">for</span> <span class="op">(</span><span class="dt">char</span> c <span class="op">:</span> part<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-34"><a href="#cb7-34"></a>            std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> c;</span>
<span id="cb7-35"><a href="#cb7-35"></a>        <span class="op">}</span></span>
<span id="cb7-36"><a href="#cb7-36"></a>        delim <span class="op">=</span> <span class="st">&quot;, &quot;</span>;</span>
<span id="cb7-37"><a href="#cb7-37"></a>    <span class="op">}</span></span>
<span id="cb7-38"><a href="#cb7-38"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;]</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb7-39"><a href="#cb7-39"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
<p>This took me more time to write than any of the solutions in any of the other languages. Including the Go solution, which contains 100% of all the lines of Go I’ve written in my life.</p>
<p>Printing is a fairly fundamental and universal mechanism to see what’s going on in your program. In the context of ranges, it’s probably the most useful way to see and understand what the various range adapters actually do. But none of these things provides an <code class="sourceCode cpp"><span class="kw">operator</span><span class="op">&lt;&lt;</span></code> (for <code class="sourceCode cpp">std<span class="op">::</span>cout</code>) or a formatter specialization (for <code class="sourceCode cpp">format</code>). And the further problem is that as a user, I can’t even do anything about this. I can’t just provide an <code class="sourceCode cpp"><span class="kw">operator</span><span class="op">&lt;&lt;</span></code> in <code class="sourceCode cpp"><span class="kw">namespace</span> std</code> or a very broad specialization of <code class="sourceCode cpp">formatter</code> - none of these are program-defined types, so it’s just asking for clashes once you start dealing with bigger programs.</p>
<p>The only mechanisms I have at my disposal to print something like this is either</p>
<ol type="1">
<li>nested loops with hand-written delimiter handling (which are tedious and a bad solution), or</li>
<li>at least replace the inner-most loop with a <code class="sourceCode cpp">ranges<span class="op">::</span>copy</code> into an output iterator (which is more differently bad), or</li>
<li>Write my own formatting library that I <em>am</em> allowed to specialize (which is not only bad but also ridiculous)</li>
<li>Use <code class="sourceCode cpp">fmt<span class="op">::</span>format</code>.</li>
</ol>
<h2 data-number="2.1" id="implementation-experience"><span class="header-section-number">2.1</span> Implementation Experience<a href="#implementation-experience" class="self-link"></a></h2>
<p>That’s right, there’s a fourth option for C++ that I haven’t shown yet, and that’s this:</p>
<blockquote>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1"></a><span class="pp">#include </span><span class="im">&lt;ranges&gt;</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="pp">#include </span><span class="im">&lt;string&gt;</span></span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="pp">#include </span><span class="im">&lt;fmt/ranges.h&gt;</span></span>
<span id="cb8-4"><a href="#cb8-4"></a></span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb8-6"><a href="#cb8-6"></a>    std<span class="op">::</span>string s <span class="op">=</span> <span class="st">&quot;xyx&quot;</span>;</span>
<span id="cb8-7"><a href="#cb8-7"></a>    <span class="kw">auto</span> parts <span class="op">=</span> s <span class="op">|</span> std<span class="op">::</span>views<span class="op">::</span>split<span class="op">(</span><span class="ch">&#39;x&#39;</span><span class="op">)</span>;</span>
<span id="cb8-8"><a href="#cb8-8"></a></span>
<span id="cb8-9"><a href="#cb8-9"></a>    fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{}</span><span class="sc">\n</span><span class="st">&quot;</span>, parts<span class="op">)</span>;</span>
<span id="cb8-10"><a href="#cb8-10"></a>    fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;[{}]</span><span class="sc">\n</span><span class="st">&quot;</span>, fmt<span class="op">::</span>join<span class="op">(</span>parts, <span class="st">&quot;,&quot;</span><span class="op">))</span>;</span>
<span id="cb8-11"><a href="#cb8-11"></a><span class="op">}</span></span></code></pre></div>
<p>outputting</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb9-1"><a href="#cb9-1"></a>{{}, {&#39;y&#39;}}</span>
<span id="cb9-2"><a href="#cb9-2"></a>[{},{&#39;y&#39;}]</span></code></pre></div>
</blockquote>
<p>And this is great! It’s a single, easy line of code to just print arbitrary ranges (include ranges of ranges).</p>
<p>And, if I want to do something more involved, there’s also <code class="sourceCode cpp">fmt<span class="op">::</span>join</code>, which lets me specify both a format specifier and a delimiter. For instance:</p>
<blockquote>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">uint8_t</span><span class="op">&gt;</span> mac <span class="op">=</span> <span class="op">{</span><span class="bn">0xaa</span>, <span class="bn">0xbb</span>, <span class="bn">0xcc</span>, <span class="bn">0xdd</span>, <span class="bn">0xee</span>, <span class="bn">0xff</span><span class="op">}</span>;</span>
<span id="cb10-2"><a href="#cb10-2"></a>fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{:02x}</span><span class="sc">\n</span><span class="st">&quot;</span>, fmt<span class="op">::</span>join<span class="op">(</span>mac, <span class="st">&quot;:&quot;</span><span class="op">))</span>;</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb11-1"><a href="#cb11-1"></a>aa:bb:cc:dd:ee:ff</span></code></pre></div>
</blockquote>
<p><code class="sourceCode cpp">fmt<span class="op">::</span>format</code> (and <code class="sourceCode cpp">fmt<span class="op">::</span>print</code>) solves my problem completely. <code class="sourceCode cpp">std<span class="op">::</span>format</code> does not, and it should.</p>
<h1 data-number="3" style="border-bottom:1px solid #cccccc" id="proposal-considerations"><span class="header-section-number">3</span> Proposal Considerations<a href="#proposal-considerations" class="self-link"></a></h1>
<p>The Ranges Plan for C++23 <span class="citation" data-cites="P2214R0">[<a href="#ref-P2214R0" role="doc-biblioref">P2214R0</a>]</span> listed as one of its top priorities for C++23 as the ability to format all views. Let’s go through the issues we need to address in order to get this functionality.</p>
<h2 data-number="3.1" id="what-types-to-print"><span class="header-section-number">3.1</span> What types to print?<a href="#what-types-to-print" class="self-link"></a></h2>
<p>The standard library is the only library that can provide formatting support for standard library types and other broad classes of types like ranges. In addition to ranges (both the conrete containers like <code class="sourceCode cpp">vector<span class="op">&lt;</span>T<span class="op">&gt;</span></code> and the range adaptors like <code class="sourceCode cpp">views<span class="op">::</span>split</code>), there are several very commonly used types that are currently not printable.</p>
<p>The most common and important such types are <code class="sourceCode cpp">pair</code> and <code class="sourceCode cpp">tuple</code> (which ties back into Ranges even more closely once we adopt <code class="sourceCode cpp">views<span class="op">::</span>zip</code> and <code class="sourceCode cpp">views<span class="op">::</span>enumerate</code>). <code class="sourceCode cpp">fmt</code> currently supports printing such types as well:</p>
<blockquote>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1"></a>fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{}</span><span class="sc">\n</span><span class="st">&quot;</span>, std<span class="op">::</span>pair<span class="op">(</span><span class="dv">1</span>, <span class="dv">2</span><span class="op">))</span>;</span></code></pre></div>
<p>outputs</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb13-1"><a href="#cb13-1"></a>(1, 2)</span></code></pre></div>
</blockquote>
<p>Another common and important set of types are <code class="sourceCode cpp">std<span class="op">::</span>optional<span class="op">&lt;</span>T<span class="op">&gt;</span></code> and <code class="sourceCode cpp">std<span class="op">::</span>variant<span class="op">&lt;</span>Ts<span class="op">...&gt;</span></code>. <code class="sourceCode cpp">fmt</code> does not support printing any of the sum types. There is not an obvious representation for them in C++ as there might be in other languages (e.g. in Rust, an <code class="sourceCode cpp">Option<span class="op">&lt;</span>i32<span class="op">&gt;</span></code> prints as either <code class="sourceCode cpp">Some<span class="op">(</span><span class="dv">42</span><span class="op">)</span></code> or <code class="sourceCode cpp">None</code>, which is also the same syntax used to construct them).</p>
<p>However, the point here isn’t necessarily to produce the best possible representation (users who have very specific formatting needs will need to write custom code anyway), but rather to provide something useful. And it’d be useful to print these types as well. However, given that <code class="sourceCode cpp">optional</code> and <code class="sourceCode cpp">variant</code> are both less closely related to Ranges than <code class="sourceCode cpp">pair</code> and <code class="sourceCode cpp">tuple</code> and also have less obvious representation, they are less important.</p>
<h2 data-number="3.2" id="what-representation"><span class="header-section-number">3.2</span> What representation?<a href="#what-representation" class="self-link"></a></h2>
<p>There are several questions to ask about what the representation should be for printing. I’ll go through each kind in turn.</p>
<h3 data-number="3.2.1" id="vector-and-other-ranges"><span class="header-section-number">3.2.1</span> <code class="sourceCode cpp">vector</code> (and other ranges)<a href="#vector-and-other-ranges" class="self-link"></a></h3>
<p>Should <code class="sourceCode cpp">std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code> be printed as <code class="sourceCode cpp"><span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code> or <code class="sourceCode cpp"><span class="op">[</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">]</span></code>? At the time of <span class="citation" data-cites="P2286R1">[<a href="#ref-P2286R1" role="doc-biblioref">P2286R1</a>]</span>, <code class="sourceCode cpp">fmt</code> used <code class="sourceCode cpp"><span class="op">{}</span></code>s but changed to use <code class="sourceCode cpp"><span class="op">[]</span></code>s for consistency with Python (<a href="https://github.com/fmtlib/fmt/commit/400b953fbb420ff1e47565303c64223445a51955">400b953f</a>).</p>
<p>Even though in C++ we initialize <code class="sourceCode cpp">vector</code>s (and, generally, other containers as well) with <code class="sourceCode cpp"><span class="op">{}</span></code>s while Python’s uses <code class="sourceCode cpp"><span class="op">[</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">]</span></code> (and likewise Rust has <code class="sourceCode cpp">vec<span class="op">![</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">]</span></code>), <code class="sourceCode cpp"><span class="op">[]</span></code> is typical representationally so seems like the clear best choice here.</p>
<h3 data-number="3.2.2" id="pair-and-tuple"><span class="header-section-number">3.2.2</span> <code class="sourceCode cpp">pair</code> and <code class="sourceCode cpp">tuple</code><a href="#pair-and-tuple" class="self-link"></a></h3>
<p>Should <code class="sourceCode cpp">std<span class="op">::</span>pair<span class="op">&lt;</span><span class="dt">int</span>, <span class="dt">int</span><span class="op">&gt;{</span><span class="dv">4</span>, <span class="dv">5</span><span class="op">}</span></code> be printed as <code class="sourceCode cpp"><span class="op">{</span><span class="dv">4</span>, <span class="dv">5</span><span class="op">}</span></code> or <code class="sourceCode cpp"><span class="op">(</span><span class="dv">4</span>, <span class="dv">5</span><span class="op">)</span></code>? Here, either syntax can claim to be the syntax used to initialize the <code class="sourceCode cpp">pair</code>/<code class="sourceCode cpp">tuple</code>. <code class="sourceCode cpp">fmt</code> has always printed these types with <code class="sourceCode cpp"><span class="op">()</span></code>s, and this is also how Python and Rust print such types. As with using <code class="sourceCode cpp"><span class="op">[]</span></code> for ranges, <code class="sourceCode cpp"><span class="op">()</span></code> seems like the common representation for tuples and so seems like the clear best choice.</p>
<h3 data-number="3.2.3" id="map-and-set-and-other-associative-containers"><span class="header-section-number">3.2.3</span> <code class="sourceCode cpp">map</code> and <code class="sourceCode cpp">set</code> (and other associative containers)<a href="#map-and-set-and-other-associative-containers" class="self-link"></a></h3>
<p>Should <code class="sourceCode cpp">std<span class="op">::</span>map<span class="op">&lt;</span><span class="dt">int</span>, <span class="dt">int</span><span class="op">&gt;{</span>{<span class="dv">1</span>, <span class="dv">2</span><span class="op">}</span>, <span class="op">{</span><span class="dv">3</span>, <span class="dv">4</span><span class="op">}}</span></code> be printed as <code class="sourceCode cpp"><span class="op">[(</span><span class="dv">1</span>, <span class="dv">2</span><span class="op">)</span>, <span class="op">(</span><span class="dv">3</span>, <span class="dv">4</span><span class="op">)]</span></code> (as follows directly from the two previous choices) or as <code class="sourceCode cpp"><span class="op">{</span><span class="dv">1</span><span class="op">:</span> <span class="dv">2</span>, <span class="dv">3</span><span class="op">:</span> <span class="dv">4</span><span class="op">}</span></code> (which makes the <em>association</em> clearer in the printing)? Both Python and Rust print their associating containers this latter way.</p>
<p>The same question holds for sets as well as maps, it’s just a question for whether <code class="sourceCode cpp">std<span class="op">::</span>set<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code> prints as <code class="sourceCode cpp"><span class="op">[</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">]</span></code> (i.e. as any other range of <code class="sourceCode cpp"><span class="dt">int</span></code>) or <code class="sourceCode cpp"><span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code>?</p>
<p>If we print <code class="sourceCode cpp">map</code>s as any other range of pairs, there’s nothing left to do. If we print <code class="sourceCode cpp">map</code>s as associations, then we additionally have to answer the question of how user-defined associative containers can get printed in the same way. Hold onto this thought for a minute.</p>
<h3 data-number="3.2.4" id="char-and-string-and-other-string-like-types-in-ranges-or-tuples"><span class="header-section-number">3.2.4</span> <code class="sourceCode cpp"><span class="dt">char</span></code> and <code class="sourceCode cpp">string</code> (and other string-like types) in ranges or tuples<a href="#char-and-string-and-other-string-like-types-in-ranges-or-tuples" class="self-link"></a></h3>
<p>Should <code class="sourceCode cpp">pair<span class="op">&lt;</span><span class="dt">char</span>, string<span class="op">&gt;(</span><span class="ch">&#39;x&#39;</span>, <span class="st">&quot;hello&quot;</span><span class="op">)</span></code> print as <code class="x">(x, hello)</code> or <code class="x">(&#39;x&#39;, &quot;hello&quot;)</code>? Should <code class="sourceCode cpp">pair<span class="op">&lt;</span><span class="dt">char</span>, string<span class="op">&gt;(</span><span class="ch">&#39;y&#39;</span>, <span class="st">&quot;with</span><span class="sc">\n\&quot;</span><span class="st">quotes</span><span class="sc">\&quot;</span><span class="st">&quot;</span><span class="op">)</span></code> print as:</p>
<blockquote>
<div class="sourceCode" id="cb14"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb14-1"><a href="#cb14-1"></a>(y, with</span>
<span id="cb14-2"><a href="#cb14-2"></a>&quot;quotes&quot;)</span></code></pre></div>
</blockquote>
<p>or</p>
<blockquote>
<div class="sourceCode" id="cb15"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb15-1"><a href="#cb15-1"></a>(&#39;y&#39;, &quot;with\n\&quot;quotes\&quot;&quot;)</span></code></pre></div>
</blockquote>
<p>While <code class="sourceCode cpp"><span class="dt">char</span></code> and <code class="sourceCode cpp">string</code> are typically printed unquoted, it is quite common to print them quoted when contained in tuples and ranges (as Python, Rust, and <code class="sourceCode cpp">fmt</code> do). Rust escapes internal strings, so prints as <code class="sourceCode cpp"><span class="op">(</span><span class="ch">&#39;y&#39;</span>, <span class="st">&quot;with</span><span class="sc">\n\&quot;</span><span class="st">quotes</span><span class="sc">\&quot;</span><span class="st">&quot;</span><span class="op">)</span></code> (the Rust implementation of <code class="sourceCode cpp">Debug</code> for <code class="sourceCode cpp">str</code> can be found <a href="https://doc.rust-lang.org/src/core/fmt/mod.rs.html#2073-2095">here</a> which is implemented in terms of <a href="https://doc.rust-lang.org/src/core/char/methods.rs.html#405-419"><code class="sourceCode cpp">escape_debug_ext</code></a>). Following discussion of this paper and this design, Victor Zverovich implemented in this <code class="sourceCode cpp">fmt</code> as well.</p>
<p>Escaping seems like the most desirable behavior. Following Rust’s behavior, we escape <code class="sourceCode cpp">\t</code>, <code class="sourceCode cpp">\r</code>, <code class="sourceCode cpp">\n</code>, <code class="sourceCode cpp">\\</code>, <code class="sourceCode cpp"><span class="st">&quot;</span></code> (for <code class="sourceCode cpp">string</code> types only), <code class="sourceCode cpp"><span class="ch">&#39;</span></code> (for <code class="sourceCode cpp"><span class="dt">char</span></code> types only), and extended graphemes (if Unicode).</p>
<p>Also, <code class="sourceCode cpp">std<span class="op">::</span>string</code> isn’t the only string-like type: if we decide to print strings quoted, how do users opt in to this behavior for their own string-like types? And <code class="sourceCode cpp"><span class="dt">char</span></code> and <code class="sourceCode cpp">string</code> aren’t the only types that may desire to have some kind of <em>debug</em> format and some kind of regular format, how to differentiate those?</p>
<p>Moreover, it’s all well and good to have the default formatting option for a range or tuple of strings to be printing those strings escaped. But what if users want to print a range of strings <em>unescaped</em>? I’ll get back to this.</p>
<h3 data-number="3.2.5" id="format-specifiers"><span class="header-section-number">3.2.5</span> Format Specifiers<a href="#format-specifiers" class="self-link"></a></h3>
<p>One of (but hardly the only) the great selling points of <code class="sourceCode cpp">format</code> over iostreams is the ability to use specifiers. For instance, from the <code class="sourceCode cpp">fmt</code> documentation:</p>
<blockquote>
<div class="sourceCode" id="cb16"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb16-1"><a href="#cb16-1"></a>fmt<span class="op">::</span>format<span class="op">(</span><span class="st">&quot;{:&lt;30}&quot;</span>, <span class="st">&quot;left aligned&quot;</span><span class="op">)</span>;</span>
<span id="cb16-2"><a href="#cb16-2"></a><span class="co">// Result: &quot;left aligned                  &quot;</span></span>
<span id="cb16-3"><a href="#cb16-3"></a>fmt<span class="op">::</span>format<span class="op">(</span><span class="st">&quot;{:&gt;30}&quot;</span>, <span class="st">&quot;right aligned&quot;</span><span class="op">)</span>;</span>
<span id="cb16-4"><a href="#cb16-4"></a><span class="co">// Result: &quot;                 right aligned&quot;</span></span>
<span id="cb16-5"><a href="#cb16-5"></a>fmt<span class="op">::</span>format<span class="op">(</span><span class="st">&quot;{:^30}&quot;</span>, <span class="st">&quot;centered&quot;</span><span class="op">)</span>;</span>
<span id="cb16-6"><a href="#cb16-6"></a><span class="co">// Result: &quot;           centered           &quot;</span></span>
<span id="cb16-7"><a href="#cb16-7"></a>fmt<span class="op">::</span>format<span class="op">(</span><span class="st">&quot;{:*^30}&quot;</span>, <span class="st">&quot;centered&quot;</span><span class="op">)</span>;  <span class="co">// use &#39;*&#39; as a fill char</span></span>
<span id="cb16-8"><a href="#cb16-8"></a><span class="co">// Result: &quot;***********centered***********&quot;</span></span></code></pre></div>
</blockquote>
<p>Earlier revisions of this paper suggested that formatting ranges and tuples would accept no format specifiers, but there indeed are quite a few things we may want to do here (as by Tomasz Kamiński and Peter Dimov):</p>
<ul>
<li>Formatting a range of pairs as a map (the <code class="sourceCode cpp">key<span class="op">:</span> value</code> syntax rather than the <code class="sourceCode cpp"><span class="op">(</span>key, value<span class="op">)</span></code> one)</li>
<li>Formatting a range of chars as a string (i.e. to print <code class="x">hello</code> or <code class="x">&quot;hello&quot;</code> rather than <code class="x">[&#39;h&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]</code>)</li>
</ul>
<p>But these are just providing a specifier for how we format the range itself. How about how we format the elements of the range? Can I conveniently format a range of integers, printing their values as hex? Or as characters? Or print a range of chrono time points in whatever format I want? That’s fairly powerful.</p>
<p>The problem is how do we actually <em>do that</em>. After a lengthy discussion with Peter Dimov, Tim Song, and Victor Zverovich, this is what we came up with. I’ll start with a table of examples and follow up with a more detailed explanation.</p>
<p>Instead of writing a bunch of examples like <code class="sourceCode cpp">print<span class="op">(</span><span class="st">&quot;{:?}</span><span class="sc">\n</span><span class="st">&quot;</span>, v<span class="op">)</span></code>, I’m just displaying the format string in one column (the <code class="sourceCode cpp"><span class="st">&quot;{:?}&quot;</span></code> here) and the argument in another (the <code class="sourceCode cpp">v</code>):</p>
<table>
<colgroup>
<col style="width: 14%"></col>
<col style="width: 42%"></col>
<col style="width: 42%"></col>
</colgroup>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Contents</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp"><span class="st">&quot;hello&quot;</span><span class="bu">s</span></code></td>
<td><code class="x">hello</code></td>
</tr>
<tr class="even">
<td><code class="x">{:?}</code></td>
<td><code class="sourceCode cpp"><span class="st">&quot;hello&quot;</span><span class="bu">s</span></code></td>
<td><code class="x">&quot;hello&quot;</code></td>
</tr>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span><span class="st">&quot;hello&quot;</span><span class="bu">s</span>, <span class="st">&quot;world&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">[&quot;hello&quot;, &quot;world&quot;]</code></td>
</tr>
<tr class="even">
<td><code class="x">{:}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span><span class="st">&quot;hello&quot;</span><span class="bu">s</span>, <span class="st">&quot;world&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">[&quot;hello&quot;, &quot;world&quot;]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:?}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span><span class="st">&quot;hello&quot;</span><span class="bu">s</span>, <span class="st">&quot;world&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">[&quot;hello&quot;, &quot;world&quot;]</code></td>
</tr>
<tr class="even">
<td><code class="x">{:*^14}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span><span class="st">&quot;he&quot;</span><span class="bu">s</span>, <span class="st">&quot;wo&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">*[&quot;he&quot;, &quot;wo&quot;]*</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::*^14}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span><span class="st">&quot;he&quot;</span><span class="bu">s</span>, <span class="st">&quot;wo&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">[******he******, ******wo******]</code></td>
</tr>
<tr class="even">
<td><code class="x">{:}</code></td>
<td><code class="sourceCode cpp"><span class="dv">42</span></code></td>
<td><code class="x">42</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:#x}</code></td>
<td><code class="sourceCode cpp"><span class="dv">42</span></code></td>
<td><code class="x">0x2a</code></td>
</tr>
<tr class="even">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[H, e, l, l, o]</code></td>
</tr>
<tr class="even">
<td><code class="x">{::?c}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::d}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[72, 101, 108, 108, 111]</code></td>
</tr>
<tr class="even">
<td><code class="x">{::#x}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[0x48, 0x65, 0x6c, 0x6c, 0x6f]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:s}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">Hello</code></td>
</tr>
<tr class="even">
<td><code class="x">{:?s}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">&quot;Hello&quot;</code></td>
</tr>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">{</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">(42, &quot;hello&quot;)</code></td>
</tr>
<tr class="even">
<td><code class="x">{::#x:*^10}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">{</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">(0x2a, **hello***)</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:|#x|*^10}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">{</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">}</span></code></td>
<td><code class="x">(0x2a, **hello***)</code></td>
</tr>
<tr class="even">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>pair<span class="op">{</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">}}</span></code></td>
<td><code class="x">[(42, &quot;hello&quot;)]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:m}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>pair<span class="op">{</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">}}</span></code></td>
<td><code class="x">{42: &quot;hello&quot;}</code></td>
</tr>
<tr class="even">
<td><code class="x">{:m::#x:*^10}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>pair<span class="op">{</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">}}</span></code></td>
<td><code class="x">{0x2a: **hello***}</code></td>
</tr>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}}</span></code></td>
<td><code class="x">[[&#39;a&#39;], [&#39;b&#39;, &#39;c&#39;]]</code></td>
</tr>
<tr class="even">
<td><code class="x">{::?s}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}}</span></code></td>
<td><code class="x">[&quot;a&quot;, &quot;bc&quot;]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:::d}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}}</span></code></td>
<td><code class="x">[[97], [98, 99]]</code></td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{}</span></code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span>system_clock<span class="op">::</span>now<span class="op">()</span>, system_clock<span class="op">::</span>now<span class="op">())</span></code></td>
<td><code class="x">(2021-10-24 20:33:37, 2021-10-24 20:33:37)</code></td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{:|%</span>Y<span class="op">-%</span>m<span class="op">-%</span>d<span class="op">|%</span>H<span class="op">:%</span>M<span class="op">:%</span>S<span class="op">}</span></code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span>system_clock<span class="op">::</span>now<span class="op">()</span>, system_clock<span class="op">::</span>now<span class="op">())</span></code></td>
<td><code class="x">(2021-10-24, 20:33:37)</code></td>
</tr>
</tbody>
</table>
<h3 data-number="3.2.6" id="explanation-of-added-specifiers"><span class="header-section-number">3.2.6</span> Explanation of Added Specifiers<a href="#explanation-of-added-specifiers" class="self-link"></a></h3>
<h4 data-number="3.2.6.1" id="the-debug-specifier"><span class="header-section-number">3.2.6.1</span> The debug specifier <code class="sourceCode cpp"><span class="op">?</span></code><a href="#the-debug-specifier" class="self-link"></a></h4>
<p><code class="sourceCode cpp"><span class="dt">char</span></code> and <code class="sourceCode cpp">string</code> and <code class="sourceCode cpp">string_view</code> will start to support the <code class="sourceCode cpp"><span class="op">?</span></code> specifier. This will cause the character/string to be printed as quoted (characters with <code class="sourceCode cpp"><span class="ch">&#39;</span></code> and strings with <code class="sourceCode cpp"><span class="st">&quot;</span></code>) and all characters to be escaped, as <a href="char-and-string-and-other-string-like-types-in-ranges-or-tuples">described earlier</a>.</p>
<p>This facility will be generated by the formatters for these types providing an addition member function (on top of <code class="sourceCode cpp">parse</code> and <code class="sourceCode cpp">format</code>):</p>
<blockquote>
<div class="sourceCode" id="cb17"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb17-1"><a href="#cb17-1"></a><span class="dt">void</span> format_as_debug<span class="op">()</span>;</span></code></pre></div>
</blockquote>
<p>Which other formatting types may conditionally invoke when they parse a <code class="sourceCode cpp"><span class="op">?</span></code>. For instance, since the intent is that range formatters print escaped by default, the logic for a simple range formatter that accepts no specifiers might look like this (note that this paper is proposing something more complicated than this, this is just an example):</p>
<blockquote>
<div class="sourceCode" id="cb18"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb18-1"><a href="#cb18-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> V<span class="op">&gt;</span></span>
<span id="cb18-2"><a href="#cb18-2"></a><span class="kw">struct</span> range_formatter <span class="op">{</span></span>
<span id="cb18-3"><a href="#cb18-3"></a>    std<span class="op">::</span>formatter<span class="op">&lt;</span>V<span class="op">&gt;</span> underlying;</span>
<span id="cb18-4"><a href="#cb18-4"></a></span>
<span id="cb18-5"><a href="#cb18-5"></a>    <span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> ParseContext<span class="op">&gt;</span></span>
<span id="cb18-6"><a href="#cb18-6"></a>    <span class="kw">constexpr</span> <span class="kw">auto</span> parse<span class="op">(</span>ParseContext<span class="op">&amp;</span> ctx<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-7"><a href="#cb18-7"></a>        <span class="co">// ensure that the format specifier is empty</span></span>
<span id="cb18-8"><a href="#cb18-8"></a>        <span class="cf">if</span> <span class="op">(</span>ctx<span class="op">.</span>begin<span class="op">()</span> <span class="op">!=</span> ctx<span class="op">.</span>end<span class="op">()</span> <span class="op">&amp;&amp;</span> <span class="op">*</span>ctx<span class="op">.</span>begin<span class="op">()</span> <span class="op">!=</span> <span class="ch">&#39;}&#39;</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-9"><a href="#cb18-9"></a>            <span class="cf">throw</span> std<span class="op">::</span>format_error<span class="op">(</span><span class="st">&quot;invalid format&quot;</span><span class="op">)</span>;</span>
<span id="cb18-10"><a href="#cb18-10"></a>        <span class="op">}</span></span>
<span id="cb18-11"><a href="#cb18-11"></a></span>
<span id="cb18-12"><a href="#cb18-12"></a>        <span class="co">// ensure that the underlying type can parse an empty specifier</span></span>
<span id="cb18-13"><a href="#cb18-13"></a>        <span class="kw">auto</span> out <span class="op">=</span> underlying<span class="op">.</span>parse<span class="op">(</span>ctx<span class="op">)</span>;</span>
<span id="cb18-14"><a href="#cb18-14"></a></span>
<span id="cb18-15"><a href="#cb18-15"></a>        <span class="co">// conditionally format as debug, if the type supports it</span></span>
<span id="cb18-16"><a href="#cb18-16"></a>        <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><span class="kw">requires</span> <span class="op">{</span> underlying<span class="op">.</span>format_as_debug<span class="op">()</span>; <span class="op">})</span> <span class="op">{</span></span>
<span id="cb18-17"><a href="#cb18-17"></a>            underlying<span class="op">.</span>format_as_debug<span class="op">()</span>;</span>
<span id="cb18-18"><a href="#cb18-18"></a>        <span class="op">}</span></span>
<span id="cb18-19"><a href="#cb18-19"></a>        <span class="cf">return</span> out;</span>
<span id="cb18-20"><a href="#cb18-20"></a>    <span class="op">}</span></span>
<span id="cb18-21"><a href="#cb18-21"></a></span>
<span id="cb18-22"><a href="#cb18-22"></a>    <span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> R, <span class="kw">typename</span> FormatContext<span class="op">&gt;</span></span>
<span id="cb18-23"><a href="#cb18-23"></a>        <span class="kw">requires</span> std<span class="op">::</span>same_as<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>range_reference_t<span class="op">&lt;</span>R<span class="op">&gt;&gt;</span>, V<span class="op">&gt;</span></span>
<span id="cb18-24"><a href="#cb18-24"></a>    <span class="kw">constexpr</span> <span class="kw">auto</span> format<span class="op">(</span>R<span class="op">&amp;&amp;</span> r, FormatContext<span class="op">&amp;</span> ctx<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-25"><a href="#cb18-25"></a>        <span class="kw">auto</span> out <span class="op">=</span> ctx<span class="op">.</span>out<span class="op">()</span>;</span>
<span id="cb18-26"><a href="#cb18-26"></a>        <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;[&#39;</span>;</span>
<span id="cb18-27"><a href="#cb18-27"></a>        <span class="kw">auto</span> first <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb18-28"><a href="#cb18-28"></a>        <span class="kw">auto</span> last <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb18-29"><a href="#cb18-29"></a>        <span class="cf">if</span> <span class="op">(</span>first <span class="op">!=</span> last<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-30"><a href="#cb18-30"></a>            <span class="co">// have to format every element via the underlying formatter</span></span>
<span id="cb18-31"><a href="#cb18-31"></a>            ctx<span class="op">.</span>advance_to<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb18-32"><a href="#cb18-32"></a>            out <span class="op">=</span> underlying<span class="op">.</span>format<span class="op">(*</span>first, ctx<span class="op">)</span>;</span>
<span id="cb18-33"><a href="#cb18-33"></a>            <span class="cf">for</span> <span class="op">(++</span>first; first <span class="op">!=</span> last; <span class="op">++</span>first<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-34"><a href="#cb18-34"></a>                <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;,&#39;</span>;</span>
<span id="cb18-35"><a href="#cb18-35"></a>                <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39; &#39;</span>;</span>
<span id="cb18-36"><a href="#cb18-36"></a>                ctx<span class="op">.</span>advance_to<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb18-37"><a href="#cb18-37"></a>                out <span class="op">=</span> underlying<span class="op">.</span>format<span class="op">(*</span>first, ctx<span class="op">)</span>;</span>
<span id="cb18-38"><a href="#cb18-38"></a>            <span class="op">}</span></span>
<span id="cb18-39"><a href="#cb18-39"></a>        <span class="op">}</span></span>
<span id="cb18-40"><a href="#cb18-40"></a>        <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;]&#39;</span>;</span>
<span id="cb18-41"><a href="#cb18-41"></a>        <span class="cf">return</span> out;</span>
<span id="cb18-42"><a href="#cb18-42"></a>    <span class="op">}</span></span>
<span id="cb18-43"><a href="#cb18-43"></a><span class="op">}</span>;</span></code></pre></div>
</blockquote>
<h4 data-number="3.2.6.2" id="range-specifiers"><span class="header-section-number">3.2.6.2</span> Range specifiers<a href="#range-specifiers" class="self-link"></a></h4>
<p>Range format specifiers come in two kinds: specifiers for the range itself and specifiers for the underlying elements of the range. They must be provided in order: the range specifiers (optionally), then if desired, a colon and then the underlying specifier (optionally). For instance:</p>
<table>
<colgroup>
<col style="width: 50%"></col>
<col style="width: 50%"></col>
</colgroup>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>specifier</strong>
</div></th>
<th><div style="text-align:center">
<strong>meaning</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="x">{}</code></td>
<td>No specifiers</td>
</tr>
<tr class="even">
<td><code class="x">{:}</code></td>
<td>No specifiers</td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{:&lt;</span><span class="dv">10</span><span class="op">}</span></code></td>
<td>The whole range formatting is left-aligned, with a width of 10</td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{:*^</span><span class="dv">20</span><span class="op">}</span></code></td>
<td>The whole range formatting is center-aligned, with a width of 20, padded with <code class="sourceCode cpp"><span class="op">*</span></code>s</td>
</tr>
<tr class="odd">
<td><code class="x">{:m}</code></td>
<td>Apply the <code class="sourceCode cpp">m</code> specifier to the range</td>
</tr>
<tr class="even">
<td><code class="x">{::d}</code></td>
<td>Apply the <code class="sourceCode cpp">d</code> specifier to each element of the range</td>
</tr>
<tr class="odd">
<td><code class="x">{:?s}</code></td>
<td>Apply the <code class="sourceCode cpp"><span class="op">?</span>s</code> specifier to the range</td>
</tr>
<tr class="even">
<td><code class="x">{:m::#x:#x}</code></td>
<td>Apply the <code class="sourceCode cpp">m</code> specifier to the range and the <code class="x">:#x:#x</code> specifier to each element of the range</td>
</tr>
</tbody>
</table>
<p>There are only a few top-level range-specific specifiers proposed:</p>
<ul>
<li><code class="sourceCode cpp">s</code>: for ranges of char, only: formats the range as a string.</li>
<li><code class="sourceCode cpp"><span class="op">?</span>s</code> for ranges of char, only: same as <code class="sourceCode cpp">s</code> except will additionally quote and escape the string</li>
<li><code class="sourceCode cpp">m</code>: for ranges of <code class="sourceCode cpp">pair</code>s (or <code class="sourceCode cpp">tuple</code>s of size 2) will format as <code class="sourceCode cpp"><span class="op">{</span>k1<span class="op">:</span> v1, k2<span class="op">:</span> v2<span class="op">}</span></code> instead of <code class="sourceCode cpp"><span class="op">[(</span>k1, v1<span class="op">)</span>, <span class="op">(</span>k2, v2<span class="op">)]</span></code> (i.e. as a <code class="sourceCode cpp">map</code>).</li>
<li><code class="sourceCode cpp">e</code>: will format without the <code class="sourceCode cpp"><span class="op">[]</span></code>s. This will let you, for instance, format a range as <code class="sourceCode cpp">a, b, c</code> or <code class="sourceCode cpp"><span class="op">{</span>a, b, c<span class="op">}</span></code> or <code class="sourceCode cpp"><span class="op">(</span>a, b, c<span class="op">)</span></code> or however else you want, simply by providing the desired format string.</li>
</ul>
<p>Additionally, ranges will support the same fill/align/width specifiers as in <em>std-format-spec</em>, for convenience and consistency.</p>
<p>If no element-specific formatter is provided (i.e. there is no inner colon - an empty element-specific formatter is still an element-specific formatter), the range will be formatted as debug. Otherwise, the element-specific formatter will be parsed and used.</p>
<p>To revisit a few rows from the earlier table:</p>
<table>
<colgroup>
<col style="width: 11%"></col>
<col style="width: 44%"></col>
<col style="width: 44%"></col>
</colgroup>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Contents</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]</code></td>
</tr>
<tr class="even">
<td><code class="x">{::}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[H, e, l, l, o]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::?c}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]</code></td>
</tr>
<tr class="even">
<td><code class="x">{::d}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[72, 101, 108, 108, 111]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::#x}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;e&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">[0x48, 0x65, 0x6c, 0x6c, 0x6f]</code></td>
</tr>
<tr class="even">
<td><code class="x">{:s}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;</span><span class="sc">\t</span><span class="ch">&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">H    llo</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:?s}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;{</span><span class="ch">&#39;H&#39;</span>, <span class="ch">&#39;</span><span class="sc">\t</span><span class="ch">&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;l&#39;</span>, <span class="ch">&#39;o&#39;</span><span class="op">}</span></code></td>
<td><code class="x">&quot;H\tllo&quot;</code></td>
</tr>
<tr class="even">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}}</span></code></td>
<td><code class="x">[[&#39;a&#39;], [&#39;b&#39;, &#39;c&#39;]]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::?s}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}}</span></code></td>
<td><code class="x">[&quot;a&quot;, &quot;bc&quot;]</code></td>
</tr>
<tr class="even">
<td><code class="x">{:::d}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}}</span></code></td>
<td><code class="x">[[97], [98, 99]]</code></td>
</tr>
</tbody>
</table>
<p>The second row is not printed quoted, because an empty element specifier is provided. The third row is printed quoted again because it was explicitly asked for using the <code class="sourceCode cpp"><span class="op">?</span>c</code> specifier, applied to each character.</p>
<p>The last row, <code class="sourceCode cpp"><span class="op">:::</span>d</code>, is parsed as:</p>
<table>
<thead>
<tr class="header">
<th></th>
<th><div style="text-align:center">
<strong>top level outer vector</strong>
</div></th>
<th></th>
<th><div style="text-align:center">
<strong>top level inner vector</strong>
</div></th>
<th></th>
<th><div style="text-align:center">
<strong>inner vector each element</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td>(none)</td>
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td>(none)</td>
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td><code class="sourceCode cpp">d</code></td>
</tr>
</tbody>
</table>
<p>That is, the <code class="sourceCode cpp">d</code> format specifier is applied to each underlying <code class="sourceCode cpp"><span class="dt">char</span></code>, which causes them to be printed as integers instead of characters.</p>
<p>Note that you can provide both a fill/align/width specifier to the range itself as well as to each element:</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Contents</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code></td>
<td><code class="x">[1, 2, 3]</code></td>
</tr>
<tr class="even">
<td><code class="x">{::*^5}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code></td>
<td><code class="x">[**1**, **2**, **3**]</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:o^17}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code></td>
<td><code class="x">oooo[1, 2, 3]oooo</code></td>
</tr>
<tr class="even">
<td><code class="x">{:o^29:*^5}</code></td>
<td><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span></code></td>
<td><code class="x">oooo[**1**, **2**, **3**]oooo</code></td>
</tr>
</tbody>
</table>
<h4 data-number="3.2.6.3" id="pair-and-tuple-specifiers"><span class="header-section-number">3.2.6.3</span> Pair and Tuple Specifiers<a href="#pair-and-tuple-specifiers" class="self-link"></a></h4>
<p>This is the hard part.</p>
<p>To start with, we for consistency will support the same fill/align/width specifiers as usual.</p>
<p>But for ranges, we can have the underlying element’s <code class="sourceCode cpp">formatter</code> simply parse the whole format specifier string from the character past the <code class="sourceCode cpp"><span class="op">:</span></code> to the <code class="sourceCode cpp"><span class="op">}</span></code>. The range doesn’t care anymore at that point, and what we’re left with is a specifier that the underlying element should understand (or not).</p>
<p>For <code class="sourceCode cpp">pair</code>, it’s not so easy, because format strings can contain <em>anything</em>. Absolutely anything. So when trying to parse a format specifier for a <code class="sourceCode cpp">pair<span class="op">&lt;</span>X, Y<span class="op">&gt;</span></code>, how do you know where <code class="sourceCode cpp">X</code>’s format specifier ends and <code class="sourceCode cpp">Y</code>’s format specifier begins? This is, in general, impossible.</p>
<p>But Tim’s insight was to take a page out of <code class="sourceCode cpp">sed</code>’s book and rely on the user providing the specifier string to actually know what they’re doing, and thus provide their own delimiter. <code class="sourceCode cpp">pair</code> will recognize the first character that is not one of its formatters as the delimiter, and then delimit based on that.</p>
<p>Let’s start with some easy examples:</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Contents</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">10</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">(10, 1729)</code></td>
</tr>
<tr class="even">
<td><code class="x">{:}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">10</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">(10, 1729)</code></td>
</tr>
<tr class="odd">
<td><code class="x">{::#x:04X}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">10</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">(0xa, 06C1)</code></td>
</tr>
<tr class="even">
<td><code class="x">{:|#x|04X}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">10</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">(0xa, 06C1)</code></td>
</tr>
<tr class="odd">
<td><code class="x">{:Y#xY04X}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">10</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">(0xa, 06C1)</code></td>
</tr>
</tbody>
</table>
<p>In the first two rows, there are no specifiers for the underlying elements. The last three rows each provide the same specifiers, but use a different delimiter:</p>
<table>
<thead>
<tr class="header">
<th></th>
<th><div style="text-align:center">
<strong>pair specifier</strong>
</div></th>
<th><div style="text-align:center">
<strong>delimiter</strong>
</div></th>
<th><div style="text-align:center">
<strong><code class="sourceCode cpp">first</code> specifier</strong>
</div></th>
<th><div style="text-align:center">
<strong>delimiter</strong>
</div></th>
<th><div style="text-align:center">
<strong><code class="sourceCode cpp">second</code> specifier</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td>(none)</td>
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td><code class="x">#x</code></td>
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td><code class="x">04X</code></td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td>(none)</td>
<td><code class="sourceCode cpp"><span class="op">|</span></code></td>
<td><code class="x">#x</code></td>
<td><code class="sourceCode cpp"><span class="op">|</span></code></td>
<td><code class="x">04X</code></td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td>(none)</td>
<td><code class="sourceCode cpp">Y</code></td>
<td><code class="x">#x</code></td>
<td><code class="sourceCode cpp">Y</code></td>
<td><code class="x">04X</code></td>
</tr>
</tbody>
</table>
<p>If you provide the <code class="sourceCode cpp">first</code> specifier, you must provide all the specifiers. In other words, <code class="x">::#x</code> would be an invalid format specifier for a <code class="sourceCode cpp">pair<span class="op">&lt;</span><span class="dt">int</span>, <span class="dt">int</span><span class="op">&gt;</span></code>.</p>
<p>To demonstrate why such a scheme is necessary, and simply using <code class="sourceCode cpp"><span class="op">:</span></code> as a delimiter is insufficient, consider chrono formatters. Chrono format strings allow anything, including <code class="sourceCode cpp"><span class="op">:</span></code>. Consider trying to format <code class="sourceCode cpp">std<span class="op">::</span>chrono<span class="op">::</span>system_clock<span class="op">::</span>now<span class="op">()</span></code> using various specifiers:</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{}</span></code></td>
<td><code class="x">2021-10-24 20:33:37</code></td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{:%</span>Y<span class="op">-%</span>m<span class="op">-%</span>d<span class="op">}</span></code></td>
<td><code class="x">2021-10-24</code></td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{:%</span>H<span class="op">:%</span>M<span class="op">:%</span>S<span class="op">}</span></code></td>
<td><code class="x">20:33:37</code></td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{:%</span>H hours, <span class="op">%</span>M minutes, <span class="op">%</span>S seconds<span class="op">}</span></code></td>
<td><code class="x">20 hours, 33 minutes, 37 seconds</code></td>
</tr>
</tbody>
</table>
<p>How could <code class="sourceCode cpp">pair</code> <em>possibly</em> know when to stop parsing <code class="sourceCode cpp">first</code>’s specifier given… that? It can’t. But if allow an arbitrary choice of delimiter, the user can pick one that won’t interfere:</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Contents</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="x">{}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span>now<span class="op">()</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">(2021-10-24 20:33:37, 1729)</code></td>
</tr>
<tr class="even">
<td><code class="x">{:m|%Y-%m-%d|#x}</code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span>now<span class="op">()</span>, <span class="dv">1729</span><span class="op">)</span></code></td>
<td><code class="x">2021-10-24: 0x6c1</code></td>
</tr>
</tbody>
</table>
<p>Which is parsed as:</p>
<table>
<thead>
<tr class="header">
<th></th>
<th><div style="text-align:center">
<strong>pair specifier</strong>
</div></th>
<th><div style="text-align:center">
<strong>delimiter</strong>
</div></th>
<th><div style="text-align:center">
<strong><code class="sourceCode cpp">first</code> specifier</strong>
</div></th>
<th><div style="text-align:center">
<strong>delimiter</strong>
</div></th>
<th><div style="text-align:center">
<strong><code class="sourceCode cpp">second</code> specifier</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">:</span></code></td>
<td><code class="sourceCode cpp">m</code></td>
<td><code class="sourceCode cpp"><span class="op">|</span></code></td>
<td><code class="sourceCode cpp"><span class="op">%</span>Y<span class="op">-%</span>m<span class="op">-%</span>d</code></td>
<td><code class="sourceCode cpp"><span class="op">|</span></code></td>
<td><code class="x">#x</code></td>
</tr>
</tbody>
</table>
<p>The above also introduces the only top-level specifier for <code class="sourceCode cpp">pair</code>: <code class="sourceCode cpp">m</code>. As with Ranges described in the previous section (and, indeed, necessary to support the Ranges functionality described there), the <code class="sourceCode cpp">m</code> specifier formatters pairs and 2-tuples as associations (i.e. <code class="sourceCode cpp">k<span class="op">:</span> v</code>) instead of as a pair/tuple (i.e. <code class="sourceCode cpp"><span class="op">(</span>k, v<span class="op">)</span></code>):</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Format String</strong>
</div></th>
<th><div style="text-align:center">
<strong>Contents</strong>
</div></th>
<th><div style="text-align:center">
<strong>Formatted Output</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{}</span></code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">1</span>, <span class="dv">2</span><span class="op">)</span></code></td>
<td><code class="x">(1, 2)</code></td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{:</span>m<span class="op">}</span></code></td>
<td><code class="sourceCode cpp">pair<span class="op">(</span><span class="dv">1</span>, <span class="dv">2</span><span class="op">)</span></code></td>
<td><code class="x">1: 2</code></td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{:</span>m<span class="op">}</span></code></td>
<td><code class="sourceCode cpp">tuple<span class="op">(</span><span class="dv">1</span>, <span class="dv">2</span><span class="op">)</span></code></td>
<td><code class="x">1: 2</code></td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{}</span></code></td>
<td><code class="sourceCode cpp">tuple<span class="op">(</span><span class="dv">1</span><span class="op">)</span></code></td>
<td><code class="x">(1)</code></td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{:</span>m<span class="op">}</span></code></td>
<td><code class="sourceCode cpp">tuple<span class="op">(</span><span class="dv">1</span><span class="op">)</span></code></td>
<td>ill-formed</td>
</tr>
<tr class="even">
<td><code class="sourceCode cpp"><span class="op">{}</span></code></td>
<td><code class="sourceCode cpp">tuple<span class="op">(</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span><span class="op">)</span></code></td>
<td><code class="x">(1, 2, 3)</code></td>
</tr>
<tr class="odd">
<td><code class="sourceCode cpp"><span class="op">{:</span>m<span class="op">}</span></code></td>
<td><code class="sourceCode cpp">tuple<span class="op">(</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span><span class="op">)</span></code></td>
<td>ill-formed</td>
</tr>
</tbody>
</table>
<p>Similarly to how in the debug specifier is handled by introducing a:</p>
<blockquote>
<div class="sourceCode" id="cb19"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb19-1"><a href="#cb19-1"></a><span class="dt">void</span> format_as_debug<span class="op">()</span>;</span></code></pre></div>
</blockquote>
<p>function, <code class="sourceCode cpp">pair</code> and <code class="sourceCode cpp">tuple</code> will provide a:</p>
<blockquote>
<div class="sourceCode" id="cb20"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb20-1"><a href="#cb20-1"></a><span class="dt">void</span> format_as_map<span class="op">()</span>;</span></code></pre></div>
</blockquote>
<p>function, that for <code class="sourceCode cpp">tuple</code> of size other than 2 will throw an exception (since you cannot format those as a map).</p>
<h2 data-number="3.3" id="implementation-challenges"><span class="header-section-number">3.3</span> Implementation Challenges<a href="#implementation-challenges" class="self-link"></a></h2>
<p>I implemented the range and pair/tuple portions of this proposal on top of libfmt. I chose to do it on top so that I can easily <a href="https://godbolt.org/z/o8nfvdYxM">share the implementation</a>, as such I could not implement <code class="sourceCode cpp"><span class="op">?</span></code> support for strings and char, though that is not a very interesting part of this proposal (at least as far as implementability is concerned). There were two big issues that I ran into that are worth covering.</p>
<h3 data-number="3.3.1" id="wrapping-basic_format_context-is-not-generally-possible"><span class="header-section-number">3.3.1</span> Wrapping <code class="sourceCode cpp">basic_format_context</code> is not generally possible<a href="#wrapping-basic_format_context-is-not-generally-possible" class="self-link"></a></h3>
<p>In order to be able to provide an arbitrary type’s specifiers to format a range, you have to have a <code class="sourceCode cpp">formatter<span class="op">&lt;</span>V<span class="op">&gt;</span></code> for the underlying type and use that specific <code class="sourceCode cpp">formatter</code> in order to <code class="sourceCode cpp">parse</code> the format specifier and then <code class="sourceCode cpp">format</code> into the given context. If that’s all you’re doing, this isn’t that big a deal, and I showed a simplified implementation of <code class="sourceCode cpp">range_formatter<span class="op">&lt;</span>V<span class="op">&gt;</span></code> <a href="#the-debug-specifier">earlier</a>.</p>
<p>However, if you additionally want to support fill/pad/align, then the game changes. You can’t format into the provided context - you have to format into <em>something else</em> first and then do the adjustments later. Adding padding support ends up doing something more like this:</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>No padding</strong>
</div></th>
<th><div style="text-align:center">
<strong>With padding</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><div class="sourceCode" id="cb21"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb21-1"><a href="#cb21-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> R, <span class="kw">typename</span> FormatContext<span class="op">&gt;</span></span>
<span id="cb21-2"><a href="#cb21-2"></a><span class="kw">constexpr</span> <span class="kw">auto</span> format<span class="op">(</span>R<span class="op">&amp;&amp;</span> r, FormatContext<span class="op">&amp;</span> ctx<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-3"><a href="#cb21-3"></a>    <span class="kw">auto</span> out <span class="op">=</span> ctx<span class="op">.</span>out<span class="op">()</span>;</span>
<span id="cb21-4"><a href="#cb21-4"></a>    <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;[&#39;</span>;</span>
<span id="cb21-5"><a href="#cb21-5"></a>    <span class="kw">auto</span> first <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb21-6"><a href="#cb21-6"></a>    <span class="kw">auto</span> last <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb21-7"><a href="#cb21-7"></a>    <span class="cf">if</span> <span class="op">(</span>first <span class="op">!=</span> last<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-8"><a href="#cb21-8"></a>        ctx<span class="op">.</span>advance_to<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb21-9"><a href="#cb21-9"></a>        out <span class="op">=</span> underlying<span class="op">.</span>format<span class="op">(*</span>first, ctx<span class="op">)</span>;</span>
<span id="cb21-10"><a href="#cb21-10"></a>        <span class="cf">for</span> <span class="op">(++</span>first; first <span class="op">!=</span> last; <span class="op">++</span>first<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-11"><a href="#cb21-11"></a>            <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;,&#39;</span>;</span>
<span id="cb21-12"><a href="#cb21-12"></a>            <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39; &#39;</span>;</span>
<span id="cb21-13"><a href="#cb21-13"></a>            ctx<span class="op">.</span>advance_to<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb21-14"><a href="#cb21-14"></a>            out <span class="op">=</span> underlying<span class="op">.</span>format<span class="op">(*</span>first, ctx<span class="op">)</span>;</span>
<span id="cb21-15"><a href="#cb21-15"></a>        <span class="op">}</span></span>
<span id="cb21-16"><a href="#cb21-16"></a>    <span class="op">}</span></span>
<span id="cb21-17"><a href="#cb21-17"></a>    <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;]&#39;</span>;</span>
<span id="cb21-18"><a href="#cb21-18"></a>    <span class="cf">return</span> out;</span>
<span id="cb21-19"><a href="#cb21-19"></a><span class="op">}</span></span></code></pre></div></td>
<td><div class="sourceCode" id="cb22"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb22-1"><a href="#cb22-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> R, <span class="kw">typename</span> FormatContext<span class="op">&gt;</span></span>
<span id="cb22-2"><a href="#cb22-2"></a><span class="kw">constexpr</span> <span class="kw">auto</span> format<span class="op">(</span>R<span class="op">&amp;&amp;</span> r, FormatContext<span class="op">&amp;</span> ctx<span class="op">)</span> <span class="op">{</span></span>
<span id="cb22-3"><a href="#cb22-3"></a>    fmt<span class="op">::</span>memory_buffer buf;</span>
<span id="cb22-4"><a href="#cb22-4"></a>    fmt<span class="op">::</span>basic_format_context<span class="op">&lt;</span>fmt<span class="op">::</span>appender, <span class="dt">char</span><span class="op">&gt;</span></span>
<span id="cb22-5"><a href="#cb22-5"></a>      bctx<span class="op">(</span>appender<span class="op">(</span>buf<span class="op">)</span>, ctx<span class="op">.</span>args<span class="op">()</span>, ctx<span class="op">.</span>locale<span class="op">())</span>;</span>
<span id="cb22-6"><a href="#cb22-6"></a></span>
<span id="cb22-7"><a href="#cb22-7"></a>    <span class="kw">auto</span> out <span class="op">=</span> bctx<span class="op">.</span>out<span class="op">()</span>;</span>
<span id="cb22-8"><a href="#cb22-8"></a>    <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;[&#39;</span>;</span>
<span id="cb22-9"><a href="#cb22-9"></a>    <span class="kw">auto</span> first <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb22-10"><a href="#cb22-10"></a>    <span class="kw">auto</span> last <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb22-11"><a href="#cb22-11"></a>    <span class="cf">if</span> <span class="op">(</span>first <span class="op">!=</span> last<span class="op">)</span> <span class="op">{</span></span>
<span id="cb22-12"><a href="#cb22-12"></a>        bctx<span class="op">.</span>advance_to<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb22-13"><a href="#cb22-13"></a>        out <span class="op">=</span> underlying<span class="op">.</span>format<span class="op">(*</span>first, bctx<span class="op">)</span>;</span>
<span id="cb22-14"><a href="#cb22-14"></a>        <span class="cf">for</span> <span class="op">(++</span>first; first <span class="op">!=</span> last; <span class="op">++</span>first<span class="op">)</span> <span class="op">{</span></span>
<span id="cb22-15"><a href="#cb22-15"></a>            <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;,&#39;</span>;</span>
<span id="cb22-16"><a href="#cb22-16"></a>            <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39; &#39;</span>;</span>
<span id="cb22-17"><a href="#cb22-17"></a>            bctx<span class="op">.</span>advance_to<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb22-18"><a href="#cb22-18"></a>            out <span class="op">=</span> underlying<span class="op">.</span>format<span class="op">(*</span>first, bctx<span class="op">)</span>;</span>
<span id="cb22-19"><a href="#cb22-19"></a>        <span class="op">}</span></span>
<span id="cb22-20"><a href="#cb22-20"></a>    <span class="op">}</span></span>
<span id="cb22-21"><a href="#cb22-21"></a>    <span class="op">*</span>out<span class="op">++</span> <span class="op">=</span> <span class="ch">&#39;]&#39;</span>;</span>
<span id="cb22-22"><a href="#cb22-22"></a></span>
<span id="cb22-23"><a href="#cb22-23"></a>    <span class="cf">return</span> fmt<span class="op">::</span>write<span class="op">(</span>ctx<span class="op">.</span>out<span class="op">()</span>,</span>
<span id="cb22-24"><a href="#cb22-24"></a>      fmt<span class="op">::</span>string_view<span class="op">(</span>buf<span class="op">.</span>data<span class="op">()</span>, buf<span class="op">.</span>size<span class="op">())</span>,</span>
<span id="cb22-25"><a href="#cb22-25"></a>      <span class="kw">this</span><span class="op">-&gt;</span>specs<span class="op">)</span>;</span>
<span id="cb22-26"><a href="#cb22-26"></a><span class="op">}</span></span></code></pre></div></td>
</tr>
</tbody>
</table>
<p>It’s mostly the same - we format into <code class="sourceCode cpp">bctx</code> instead of <code class="sourceCode cpp">ctx</code> and then <code class="sourceCode cpp">write</code> into <code class="sourceCode cpp">ctx</code> later using the <code class="sourceCode cpp">specs</code> that we already parsed. The code seems straightforward enough, except…</p>
<p>First, we don’t even expose a way to construct <code class="sourceCode cpp">basic_format_context</code> so can’t do this at all. Nor do we expose a way of constructing an iterator type for formatting into some buffer. And if we could construct these things, the real problem hits when we try to construct this new context. We need some kind of <code class="sourceCode cpp">fmt<span class="op">::</span>basic_format_context<span class="op">&lt;???</span>, <span class="dt">char</span><span class="op">&gt;</span></code>, and we need to write into some kind of dynamic buffer, so <code class="sourceCode cpp">fmt<span class="op">::</span>appender</code> is the appropriate choice for iterator. But the issue here is that <code class="sourceCode cpp">fmt<span class="op">::</span>basic_format_context<span class="op">&lt;</span>Out, CharT<span class="op">&gt;</span></code> has a member <code class="sourceCode cpp">fmt<span class="op">::</span>basic_format_args<span class="op">&lt;</span>basic_format_context<span class="op">&gt;</span></code> - the underlying arguments are templates <em>on the context</em>. We can’t just… change the <code class="sourceCode cpp">basic_format_args</code> to have a different context, this is a fairly fundamental attachment in the design.</p>
<p>The <em>only</em> type for the output iterator that I can support in this implementation is precisely <code class="sourceCode cpp">fmt<span class="op">::</span>appender</code>.</p>
<p>This seems like it’d be <em>extremely</em> limiting.</p>
<p>Except it turns out that actually nearly all of libfmt uses exactly this iterator. <code class="sourceCode cpp">fmt<span class="op">::</span>print</code>, <code class="sourceCode cpp">fmt<span class="op">::</span>format</code>, <code class="sourceCode cpp">fmt<span class="op">::</span>format_to</code>, <code class="sourceCode cpp">fmt<span class="op">::</span>format_to_n</code>, <code class="sourceCode cpp">fmt<span class="op">::</span>vformat</code>, etc., all only use this one iterator type. This is because of <span class="citation" data-cites="P2216R3">[<a href="#ref-P2216R3" role="doc-biblioref">P2216R3</a>]</span>’s efforts to reduce code bloat by type erasing the output iterator.</p>
<p>However, there is one part of libfmt that uses a different iterator type, which the above implementation fails on:</p>
<blockquote>
<div class="sourceCode" id="cb23"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb23-1"><a href="#cb23-1"></a>fmt<span class="op">::</span>format<span class="op">(</span><span class="st">&quot;{:::d}&quot;</span>, vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}})</span>;              <span class="co">// ok: [[97], [98, 99]]</span></span>
<span id="cb23-2"><a href="#cb23-2"></a>fmt<span class="op">::</span>format<span class="op">(</span>FMT_COMPILE<span class="op">(</span><span class="st">&quot;{:::d}&quot;</span><span class="op">)</span>, vector<span class="op">{</span>vector<span class="op">{</span><span class="ch">&#39;a&#39;</span><span class="op">}</span>, vector<span class="op">{</span><span class="ch">&#39;b&#39;</span>, <span class="ch">&#39;c&#39;</span><span class="op">}})</span>; <span class="co">// ill-formed</span></span></code></pre></div>
</blockquote>
<p>The latter fails because there the initial output iterator type is <code class="sourceCode cpp">std<span class="op">::</span>back_insert_iterator<span class="op">&lt;</span>std<span class="op">::</span>string<span class="op">&gt;</span></code>. This is a different iterator type from <code class="sourceCode cpp">fmt<span class="op">::</span>appender</code>, so we get a mismatch in the types of the <code class="sourceCode cpp">basic_format_args</code> specializations, and cannot compile the construction of <code class="sourceCode cpp">bctx</code>.</p>
<p>This can be worked around (I just need to know what the type of the buffer needs to be, in the usual case it’s <code class="sourceCode cpp">fmt<span class="op">::</span>memory_buffer</code> and here it becomes <code class="sourceCode cpp">std<span class="op">::</span>string</code>, that’s fine), but it means we really need to nail down what the requirements of the <code class="sourceCode cpp">formatter</code> API are. One of the things we need to do in this paper is provide a <code class="sourceCode cpp">formattable</code> concept. From a previous revision of that paper, dropping the <code class="sourceCode cpp"><span class="dt">char</span></code> parameter for simplicity, that looks like:</p>
<blockquote>
<div class="sourceCode" id="cb24"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb24-1"><a href="#cb24-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb24-2"><a href="#cb24-2"></a><span class="kw">concept</span> <em>formattable-impl</em> <span class="op">=</span></span>
<span id="cb24-3"><a href="#cb24-3"></a>    std<span class="op">::</span>semiregular<span class="op">&lt;</span>fmt<span class="op">::</span>formatter<span class="op">&lt;</span>T<span class="op">&gt;&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb24-4"><a href="#cb24-4"></a>    <span class="kw">requires</span> <span class="op">(</span>fmt<span class="op">::</span>formatter<span class="op">&lt;</span>T<span class="op">&gt;</span> f,</span>
<span id="cb24-5"><a href="#cb24-5"></a>              <span class="kw">const</span> T t,</span>
<span id="cb24-6"><a href="#cb24-6"></a>              fmt<span class="op">::</span>basic_format_context<span class="op">&lt;</span><span class="dt">char</span><span class="op">*</span>, <span class="dt">char</span><span class="op">&gt;</span> fc,</span>
<span id="cb24-7"><a href="#cb24-7"></a>              fmt<span class="op">::</span>basic_format_parse_context<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;</span> pc<span class="op">)</span></span>
<span id="cb24-8"><a href="#cb24-8"></a>    <span class="op">{</span></span>
<span id="cb24-9"><a href="#cb24-9"></a>        <span class="op">{</span> f<span class="op">.</span>parse<span class="op">(</span>pc<span class="op">)</span> <span class="op">}</span> <span class="op">-&gt;</span> std<span class="op">::</span>same_as<span class="op">&lt;</span>fmt<span class="op">::</span>basic_format_parse_context<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;::</span>iterator<span class="op">&gt;</span>;</span>
<span id="cb24-10"><a href="#cb24-10"></a>        <span class="op">{</span> f<span class="op">.</span>format<span class="op">(</span>t, fc<span class="op">)</span> <span class="op">}</span> <span class="op">-&gt;</span> std<span class="op">::</span>same_as<span class="op">&lt;</span><span class="dt">char</span><span class="op">*&gt;</span>;</span>
<span id="cb24-11"><a href="#cb24-11"></a>    <span class="op">}</span>;</span>
<span id="cb24-12"><a href="#cb24-12"></a></span>
<span id="cb24-13"><a href="#cb24-13"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb24-14"><a href="#cb24-14"></a><span class="kw">concept</span> formattable <span class="op">=</span> <em>formattable-impl</em><span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>T<span class="op">&gt;&gt;</span>;</span></code></pre></div>
</blockquote>
<p>I use <code class="sourceCode cpp"><span class="dt">char</span><span class="op">*</span></code> as the output iterator, but my <code class="sourceCode cpp">range_formatter<span class="op">&lt;</span>V<span class="op">&gt;</span></code> cannot support <code class="sourceCode cpp"><span class="dt">char</span><span class="op">*</span></code> as an output iterator type at all. Do <code class="sourceCode cpp">formatter</code> specializations need to support any output iterator type? If so, how can we implement fill/align/pad support in <code class="sourceCode cpp">range_formatter</code>?</p>
<p>The simplest approach would be to state that there actually is only one output iterator type that need be support per character type. This is mostly already the case in libfmt, and seems to be how MSVC implements <code class="sourceCode cpp"><span class="op">&lt;</span>format<span class="op">&gt;</span></code> as well. That is, we already have in <span>20.20.1 <a href="https://wg21.link/format.syn">[format.syn]</a></span>:</p>
<blockquote>
<div class="sourceCode" id="cb25"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb25-1"><a href="#cb25-1"></a><span class="kw">namespace</span> std <span class="op">{</span></span>
<span id="cb25-2"><a href="#cb25-2"></a>  <span class="co">// [format.context], class template basic_­format_­context</span></span>
<span id="cb25-3"><a href="#cb25-3"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Out, <span class="kw">class</span> charT<span class="op">&gt;</span> <span class="kw">class</span> basic_format_context;</span>
<span id="cb25-4"><a href="#cb25-4"></a>  <span class="kw">using</span> format_context <span class="op">=</span> basic_format_context<span class="op">&lt;</span><em>unspecified</em>, <span class="dt">char</span><span class="op">&gt;</span>;</span>
<span id="cb25-5"><a href="#cb25-5"></a>  <span class="kw">using</span> wformat_context <span class="op">=</span> basic_format_context<span class="op">&lt;</span><em>unspecified</em>, <span class="dt">wchar_t</span><span class="op">&gt;</span>;</span>
<span id="cb25-6"><a href="#cb25-6"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
<p>The suggestion would be that the only contexts that need be supported are <code class="sourceCode cpp">std<span class="op">::</span>format_context</code> and/or <code class="sourceCode cpp">std<span class="op">::</span>wformat_context</code>. Only one context for each character type.</p>
<p>That reduces the problem quite a bit, but it’s still not enough. We’re not exposing what the buffer type needs to be, so even if I knew I only had to deal with <code class="sourceCode cpp">std<span class="op">::</span>format_context</code>, I still wouldn’t know how to construct a dynamic buffer that <code class="sourceCode cpp">std<span class="op">::</span>format_context<span class="op">::</span>iterator</code> is an extending output iterator into. That is, we need to expose/standardize <code class="sourceCode cpp">fmt<span class="op">::</span>memory_buffer</code> (or provide it as an typedef somewhere).</p>
<p>If we don’t require <em>just</em> one format context per character type, we can simply throw more type erasure at the problem. Say the only allowed iterators are either (using libfmt’s names) <code class="sourceCode cpp">fmt<span class="op">::</span>appender</code> or <code class="sourceCode cpp">variant<span class="op">&lt;</span>fmt<span class="op">::</span>appender, Out<span class="op">&gt;</span></code>. The latter still allows support for other iterator types, while still letting other formatters use <code class="sourceCode cpp">fmt<span class="op">::</span>appender</code> which they know how to do. This has some cost of course, but it does provide extra flexibility.</p>
<p>At a minimum, the API we need is:</p>
<blockquote>
<div class="sourceCode" id="cb26"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb26-1"><a href="#cb26-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> V, <span class="kw">typename</span> FormatContext<span class="op">&gt;</span></span>
<span id="cb26-2"><a href="#cb26-2"></a><span class="kw">constexpr</span> <span class="kw">auto</span> format<span class="op">(</span>V<span class="op">&amp;&amp;</span> value, FormatContext<span class="op">&amp;</span> ctx<span class="op">)</span> <span class="op">-&gt;</span> <span class="kw">typename</span> FormatContext<span class="op">::</span>iterator</span>
<span id="cb26-3"><a href="#cb26-3"></a><span class="op">{</span></span>
<span id="cb26-4"><a href="#cb26-4"></a>    <span class="co">// ctx here is a basic_format_context&lt;OutIt, CharT&gt;, for some output iterator</span></span>
<span id="cb26-5"><a href="#cb26-5"></a>    <span class="co">// and some character type</span></span>
<span id="cb26-6"><a href="#cb26-6"></a></span>
<span id="cb26-7"><a href="#cb26-7"></a>    <span class="co">// can use a vector&lt;CharT&gt;, basic_string&lt;CharT&gt;, or some custom buffer like</span></span>
<span id="cb26-8"><a href="#cb26-8"></a>    <span class="co">// fmt::buffer, user&#39;s choice</span></span>
<span id="cb26-9"><a href="#cb26-9"></a>    vector<span class="op">&lt;</span>CharT<span class="op">&gt;</span> buf;</span>
<span id="cb26-10"><a href="#cb26-10"></a></span>
<span id="cb26-11"><a href="#cb26-11"></a>    <span class="co">// The retargeted_format_context class template can keep extra state if</span></span>
<span id="cb26-12"><a href="#cb26-12"></a>    <span class="co">// necessary, but bctx is still definitely a (w)format_context. The library</span></span>
<span id="cb26-13"><a href="#cb26-13"></a>    <span class="co">// ensures that regardless of the provided iterator, it gets type-erased as</span></span>
<span id="cb26-14"><a href="#cb26-14"></a>    <span class="co">// necessary</span></span>
<span id="cb26-15"><a href="#cb26-15"></a>    retargeted_format_context rctx<span class="op">(</span>ctx, std<span class="op">::</span>back_inserter<span class="op">(</span>buf<span class="op">))</span>;</span>
<span id="cb26-16"><a href="#cb26-16"></a>    <span class="kw">auto</span><span class="op">&amp;</span> bctx <span class="op">=</span> rctx<span class="op">.</span>context<span class="op">()</span>;</span>
<span id="cb26-17"><a href="#cb26-17"></a></span>
<span id="cb26-18"><a href="#cb26-18"></a>    <span class="co">// format into bctx...</span></span>
<span id="cb26-19"><a href="#cb26-19"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
<p>This can be made to work by <code class="sourceCode cpp">retargeted_format_context</code> simply doing the type erasure itself, and providing the user with the type-erased iterator result. Same as the library is already doing for all of its other entry points. For the typical case where all the entry points are already this type-erased iterator type, this is trivial. And if we allow arbitrary iterator types in the future, that entry point will have to erase both ways. Which is work, but it seems both quite feasible and in line with the rest of the design.</p>
<p>This could theoretically have been an ABI break, except that everything in the standard library today uses the one type-erased iterator (in which case the issue here is not a problem, except insofar as there is no way to actually create a new <code class="sourceCode cpp">format_context</code>).</p>
<h3 data-number="3.3.2" id="manipulating-basic_format_parse_context-to-search-for-sentinels"><span class="header-section-number">3.3.2</span> Manipulating <code class="sourceCode cpp">basic_format_parse_context</code> to search for sentinels<a href="#manipulating-basic_format_parse_context-to-search-for-sentinels" class="self-link"></a></h3>
<p>Take a look at one of the <code class="sourceCode cpp">pair</code> formatting examples:</p>
<blockquote>
<div class="sourceCode" id="cb27"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb27-1"><a href="#cb27-1"></a>fmt<span class="op">::</span>format<span class="op">(</span><span class="st">&quot;{:|#x|*^10}&quot;</span>, std<span class="op">::</span>pair<span class="op">(</span><span class="dv">42</span>, <span class="st">&quot;hello&quot;</span><span class="bu">s</span><span class="op">))</span>;</span></code></pre></div>
</blockquote>
<p>In order for this to work, the <code class="sourceCode cpp">formatter<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span></code> object needs to be passed a context that just contains the string <code class="sourceCode cpp"><span class="st">&quot;#x&quot;</span></code> and the <code class="sourceCode cpp">formatter<span class="op">&lt;</span>string<span class="op">&gt;</span></code> object needs to be passed a context that just contains the string <code class="sourceCode cpp"><span class="st">&quot;*^10&quot;</span></code> (or possibly <code class="sourceCode cpp"><span class="st">&quot;*^10}&quot;</span></code>). This is because <code class="sourceCode cpp">formatter<span class="op">&lt;</span>T<span class="op">&gt;::</span>parse</code> must consume the whole context. That’s the API.</p>
<p>But <code class="sourceCode cpp">basic_format_parse_context</code> does not provide a way for you to take a slice of it, and we can’t just construct a new object because of the dynamic argument counting support. Not just <em>any</em> context, but <em>specifically that one</em>.</p>
<p>Tim’s suggested design for how to even do specifiers for <code class="sourceCode cpp">pair</code> also came with a suggested implementation: use a <code class="sourceCode cpp">sentry</code>-like type that temporarily modifies the context and restores it later. The use of this type looks like this:</p>
<blockquote>
<div class="sourceCode" id="cb28"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb28-1"><a href="#cb28-1"></a><span class="kw">auto</span> <span class="kw">const</span> delim <span class="op">=</span> <span class="op">*</span>begin<span class="op">++</span>;</span>
<span id="cb28-2"><a href="#cb28-2"></a>ctx<span class="op">.</span>advance_to<span class="op">(</span>begin<span class="op">)</span>;</span>
<span id="cb28-3"><a href="#cb28-3"></a>tuple_for_each_index<span class="op">(</span>underlying, <span class="op">[&amp;](</span><span class="kw">auto</span> I, <span class="kw">auto</span><span class="op">&amp;</span> f<span class="op">){</span></span>
<span id="cb28-4"><a href="#cb28-4"></a>    <span class="kw">auto</span> next_delim <span class="op">=</span> std<span class="op">::</span>find<span class="op">(</span>ctx<span class="op">.</span>begin<span class="op">()</span>, end, delim<span class="op">)</span>;</span>
<span id="cb28-5"><a href="#cb28-5"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>I <span class="op">+</span> <span class="dv">1</span> <span class="op">&lt;</span> <span class="kw">sizeof</span><span class="op">...(</span>Ts<span class="op">))</span> <span class="op">{</span></span>
<span id="cb28-6"><a href="#cb28-6"></a>        <span class="cf">if</span> <span class="op">(</span>next_delim <span class="op">==</span> end<span class="op">)</span> <span class="op">{</span></span>
<span id="cb28-7"><a href="#cb28-7"></a>            <span class="cf">throw</span> fmt<span class="op">::</span>format_error<span class="op">(</span><span class="st">&quot;ran out of specifiers&quot;</span><span class="op">)</span>;</span>
<span id="cb28-8"><a href="#cb28-8"></a>        <span class="op">}</span></span>
<span id="cb28-9"><a href="#cb28-9"></a>    <span class="op">}</span></span>
<span id="cb28-10"><a href="#cb28-10"></a></span>
<span id="cb28-11"><a href="#cb28-11"></a>    end_sentry _<span class="op">(</span>ctx, next_delim<span class="op">)</span>;</span>
<span id="cb28-12"><a href="#cb28-12"></a>    <span class="kw">auto</span> i <span class="op">=</span> f<span class="op">.</span>parse<span class="op">(</span>ctx<span class="op">)</span>;</span>
<span id="cb28-13"><a href="#cb28-13"></a>    <span class="cf">if</span> <span class="op">(</span>i <span class="op">!=</span> next_delim <span class="op">&amp;&amp;</span> <span class="op">*</span>i <span class="op">!=</span> <span class="ch">&#39;}&#39;</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb28-14"><a href="#cb28-14"></a>        <span class="cf">throw</span> fmt<span class="op">::</span>format_error<span class="op">(</span><span class="st">&quot;this is broken&quot;</span><span class="op">)</span>;</span>
<span id="cb28-15"><a href="#cb28-15"></a>    <span class="op">}</span></span>
<span id="cb28-16"><a href="#cb28-16"></a></span>
<span id="cb28-17"><a href="#cb28-17"></a>    <span class="cf">if</span> <span class="op">(</span>next_delim <span class="op">!=</span> end<span class="op">)</span> <span class="op">{</span></span>
<span id="cb28-18"><a href="#cb28-18"></a>        <span class="op">++</span>i;</span>
<span id="cb28-19"><a href="#cb28-19"></a>    <span class="op">}</span></span>
<span id="cb28-20"><a href="#cb28-20"></a>    ctx<span class="op">.</span>advance_to<span class="op">(</span>i<span class="op">)</span>;</span>
<span id="cb28-21"><a href="#cb28-21"></a><span class="op">})</span>;</span></code></pre></div>
</blockquote>
<p>This ensures that each element of the <code class="sourceCode cpp">pair</code>/<code class="sourceCode cpp">tuple</code> only sees its part of the whole parse string, which is the only part that it knows what to do anything with.</p>
<p>Without something like this in the library, it’d be impossible to do this sort of complex specifier parsing. You could support ranges (there, we only have one underlying element, so it parses to the end), but not pair or tuple. We <em>could</em> say that since pair and tuple are library types, the library should just Make This Work, but there are surely other examples of wanting to do this sort of thing and it doesn’t feel right to not allow users to do it too.</p>
<p>This design space is, thankfully, slightly easier than the previous problem: this is basically what you have to do. Not much choice, I don’t think.</p>
<h3 data-number="3.3.3" id="parsing-of-alignment-padding-and-width"><span class="header-section-number">3.3.3</span> Parsing of alignment, padding, and width<a href="#parsing-of-alignment-padding-and-width" class="self-link"></a></h3>
<p>The first two issues in this section are serious implementation issues that require design changes to <code class="sourceCode cpp"><span class="op">&lt;</span>format<span class="op">&gt;</span></code>. This one doesn’t <em>require</em> changes, and this paper won’t propose changes, but it’s worth pointing out nevertheless. Alignment, padding, and width are the most common and fairly universal specifiers. But we don’t provide a public API to actually parse them.</p>
<p>When implementing this in <code class="sourceCode cpp">fmt</code>, I just took advantage of <code class="sourceCode cpp">fmt</code>’s implementation details to make this a lot easier for myself: a type (<code class="sourceCode cpp">dynamic_format_specs<span class="op">&lt;</span><span class="dt">char</span><span class="op">&gt;</span></code>) that holds all the specifier results, a function that understands those to let you write a padded/aligned string (<code class="sourceCode cpp">write</code>), and several parsing functions that are well designed to do the right thing if you have a unique set of specifiers you wish to parse (the appropriately-named <code class="sourceCode cpp">parse_align</code> and <code class="sourceCode cpp">parse_width</code>).</p>
<p>These don’t have to be standardized, as nothing in these functions is something that a user couldn’t write on their own. And this paper is big enough already, so it, again, won’t propose anything in this space. But it’s worth considering for the future.</p>
<h2 data-number="3.4" id="how-to-support-those-views-which-are-not-const-iterable"><span class="header-section-number">3.4</span> How to support those views which are not <code class="sourceCode cpp"><span class="kw">const</span></code>-iterable?<a href="#how-to-support-those-views-which-are-not-const-iterable" class="self-link"></a></h2>
<p>In a previous revision of this paper, this was a real problem since at the time <code class="sourceCode cpp">std<span class="op">::</span>format</code> accepted its arguments by <code class="sourceCode cpp"><span class="kw">const</span> Args<span class="op">&amp;...</span></code></p>
<p>However, <span class="citation" data-cites="P2418R2">[<a href="#ref-P2418R2" role="doc-biblioref">P2418R2</a>]</span> was speedily adopted specifically to address this issue, and now <code class="sourceCode cpp">std<span class="op">::</span>format</code> accepts its arguments by <code class="sourceCode cpp">Args<span class="op">&amp;&amp;...</span></code> This allows those views which are not <code class="sourceCode cpp"><span class="kw">const</span></code>-iterable to be mutably passed into <code class="sourceCode cpp">format<span class="op">()</span></code> and <code class="sourceCode cpp">print<span class="op">()</span></code> and then mutably into its formatter. To support both <code class="sourceCode cpp"><span class="kw">const</span></code> and non-<code class="sourceCode cpp"><span class="kw">const</span></code> formatting of ranges without too much boilerplate, we can do it this way:</p>
<blockquote>
<div class="sourceCode" id="cb29"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb29-1"><a href="#cb29-1"></a><span class="kw">template</span> <span class="op">&lt;</span>formattable V<span class="op">&gt;</span></span>
<span id="cb29-2"><a href="#cb29-2"></a><span class="kw">struct</span> range_formatter <span class="op">{</span></span>
<span id="cb29-3"><a href="#cb29-3"></a>    <span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> ParseContext<span class="op">&gt;</span></span>
<span id="cb29-4"><a href="#cb29-4"></a>    <span class="kw">constexpr</span> <span class="kw">auto</span> parse<span class="op">(</span>ParseContext<span class="op">&amp;)</span>;</span>
<span id="cb29-5"><a href="#cb29-5"></a></span>
<span id="cb29-6"><a href="#cb29-6"></a>    <span class="kw">template</span> <span class="op">&lt;</span>range R, <span class="kw">typename</span> FormatContext<span class="op">&gt;</span></span>
<span id="cb29-7"><a href="#cb29-7"></a>        <span class="kw">requires</span> same_as<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>range_reference_t<span class="op">&lt;</span>R<span class="op">&gt;&gt;</span>, V<span class="op">&gt;</span></span>
<span id="cb29-8"><a href="#cb29-8"></a>    <span class="kw">constexpr</span> <span class="kw">auto</span> format<span class="op">(</span>R<span class="op">&amp;&amp;</span>, FormatContext<span class="op">&amp;)</span>;</span>
<span id="cb29-9"><a href="#cb29-9"></a><span class="op">}</span>;</span>
<span id="cb29-10"><a href="#cb29-10"></a></span>
<span id="cb29-11"><a href="#cb29-11"></a><span class="kw">template</span> <span class="op">&lt;</span>range R<span class="op">&gt;</span> <span class="kw">requires</span> formattable<span class="op">&lt;</span>range_reference_t<span class="op">&lt;</span>R<span class="op">&gt;&gt;</span></span>
<span id="cb29-12"><a href="#cb29-12"></a><span class="kw">struct</span> formatter<span class="op">&lt;</span>R<span class="op">&gt;</span> <span class="op">:</span> range_formatter<span class="op">&lt;</span>range_reference_t<span class="op">&lt;</span>R<span class="op">&gt;&gt;</span></span>
<span id="cb29-13"><a href="#cb29-13"></a><span class="op">{</span> <span class="op">}</span>;</span></code></pre></div>
</blockquote>
<p><code class="sourceCode cpp">range_formatter</code> allows reducing unnecessary template instantiations. Any range of <code class="sourceCode cpp"><span class="dt">int</span></code> is going to <code class="sourceCode cpp">parse</code> its specifiers the same way, there’s no need to re-instantiate that code n times. Such a type will also help users to write their own formatters.</p>
<h2 data-number="3.5" id="what-additional-functionality"><span class="header-section-number">3.5</span> What additional functionality?<a href="#what-additional-functionality" class="self-link"></a></h2>
<p>There’s three layers of potential functionality:</p>
<ol type="1">
<li><p>Top-level printing of ranges: this is <code class="sourceCode cpp">fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{}&quot;</span>, r<span class="op">)</span></code>;</p></li>
<li><p>A format-joiner which allows providing a a custom delimiter: this is <code class="sourceCode cpp">fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{:02x}&quot;</span>, fmt<span class="op">::</span>join<span class="op">(</span>r, <span class="st">&quot;:&quot;</span><span class="op">))</span></code>. This revision of the paper allows providing a format specifier and removed in the brackets in the top-level case too, as in <code class="sourceCode cpp">fmt<span class="op">::</span>print<span class="op">(</span><span class="st">&quot;{:e:02x}&quot;</span>, r<span class="op">)</span></code>, but does not allow for providing a custom delimiter.</p></li>
<li><p>A more involved version of a format-joiner which takes a delimiter and a callback that gets invoked on each element. fmt does not provide such a mechanism, though the Rust itertools library does:</p></li>
</ol>
<blockquote>
<div class="sourceCode" id="cb7"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">let</span> matrix <span class="op">=</span> [[<span class="dv">1</span><span class="op">.,</span> <span class="dv">2</span><span class="op">.,</span> <span class="dv">3</span><span class="op">.</span>]<span class="op">,</span></span>
<span id="cb7-2"><a href="#cb7-2"></a>              [<span class="dv">4</span><span class="op">.,</span> <span class="dv">5</span><span class="op">.,</span> <span class="dv">6</span><span class="op">.</span>]]<span class="op">;</span></span>
<span id="cb7-3"><a href="#cb7-3"></a><span class="kw">let</span> matrix_formatter <span class="op">=</span> matrix<span class="op">.</span>iter()<span class="op">.</span>format_with(<span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> <span class="op">|</span>row<span class="op">,</span> f<span class="op">|</span> <span class="op">{</span></span>
<span id="cb7-4"><a href="#cb7-4"></a>                                f(<span class="op">&amp;</span>row<span class="op">.</span>iter()<span class="op">.</span>format_with(<span class="st">&quot;, &quot;</span><span class="op">,</span> <span class="op">|</span>elt<span class="op">,</span> g<span class="op">|</span> g(<span class="op">&amp;</span>elt)))</span>
<span id="cb7-5"><a href="#cb7-5"></a>                              <span class="op">}</span>)<span class="op">;</span></span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="pp">assert_eq!</span>(<span class="pp">format!</span>(<span class="st">&quot;{}&quot;</span><span class="op">,</span> matrix_formatter)<span class="op">,</span></span>
<span id="cb7-7"><a href="#cb7-7"></a>           <span class="st">&quot;1, 2, 3</span><span class="sc">\n</span><span class="st">4, 5, 6&quot;</span>)<span class="op">;</span></span></code></pre></div>
</blockquote>
<p>This paper suggests the first two and encourages research into the third.</p>
<h2 data-number="3.6" id="format-or-stdcout"><span class="header-section-number">3.6</span> <code class="sourceCode cpp">format</code> or <code class="sourceCode cpp">std<span class="op">::</span>cout</code>?<a href="#format-or-stdcout" class="self-link"></a></h2>
<p>Just <code class="sourceCode cpp">format</code> is sufficient.</p>
<h2 data-number="3.7" id="what-about-vectorbool"><span class="header-section-number">3.7</span> What about <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span></code>?<a href="#what-about-vectorbool" class="self-link"></a></h2>
<p>Nobody expected this section.</p>
<p>The <code class="sourceCode cpp">value_type</code> of this range is <code class="sourceCode cpp"><span class="dt">bool</span></code>, which is formattable. But the <code class="sourceCode cpp">reference</code> type of this range is <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;::</span>reference</code>, which is not. In order to make the whole type formattable, we can either make <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;::</span>reference</code> formattable (and thus, in general, a range is formattable if its <code class="sourceCode cpp">reference</code> types is formattable) or allow formatting to fall back to constructing a <code class="sourceCode cpp">value_type</code> for each <code class="sourceCode cpp">reference</code> (and thus, in general, a range is formattable if either its <code class="sourceCode cpp">reference</code> type or its <code class="sourceCode cpp">value_type</code> is formattable).</p>
<p>For most ranges, the <code class="sourceCode cpp">value_type</code> is <code class="sourceCode cpp">remove_cvref_t<span class="op">&lt;</span>reference<span class="op">&gt;</span></code>, so there’s no distinction here between the two options. And even for <code class="sourceCode cpp">zip</code> <span class="citation" data-cites="P2321R2">[<a href="#ref-P2321R2" role="doc-biblioref">P2321R2</a>]</span>, there’s still not much distinction since it just wraps this question in tuple since again for most ranges the types will be something like <code class="sourceCode cpp">tuple<span class="op">&lt;</span>T, U<span class="op">&gt;</span></code> vs <code class="sourceCode cpp">tuple<span class="op">&lt;</span>T<span class="op">&amp;</span>, U <span class="kw">const</span><span class="op">&amp;&gt;</span></code>, so again there isn’t much distinction.</p>
<p><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span></code> is one of the very few ranges in which the two types are truly quite different. So it doesn’t offer much in the way of a good example here, since <code class="sourceCode cpp"><span class="dt">bool</span></code> is cheaply constructible from <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;::</span>reference</code>. Though it’s also very cheap to provide a formatter specialization for <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;::</span>reference</code>.</p>
<p>Rather than having the library provide a default fallback that lifts all the <code class="sourceCode cpp">reference</code> types to <code class="sourceCode cpp">value_type</code>s, which may be arbitrarily expensive for unknown ranges, this paper proposes a format specialization for <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;::</span>reference</code>. Or, rather, since it’s actually defined as <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span>, Alloc<span class="op">&gt;::</span>reference</code>, this isn’t necessarily feasible, so instead this paper proposes a specialization for <code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span>, Alloc<span class="op">&gt;</span></code> at top level.</p>
<h1 data-number="4" style="border-bottom:1px solid #cccccc" id="proposal"><span class="header-section-number">4</span> Proposal<a href="#proposal" class="self-link"></a></h1>
<p>The standard library will provide the following utilities:</p>
<ul>
<li>A <code class="sourceCode cpp">formattable</code> concept.</li>
<li>A <code class="sourceCode cpp">range_formatter<span class="op">&lt;</span>V<span class="op">&gt;</span></code> that uses a <code class="sourceCode cpp">formatter<span class="op">&lt;</span>V<span class="op">&gt;</span></code> to <code class="sourceCode cpp">parse</code> and <code class="sourceCode cpp">format</code> a range whose <code class="sourceCode cpp">reference</code> is similar to <code class="sourceCode cpp">V</code>. This can accept a specifier on the range (align/pad/width as well as string/map/debug/empty) and on the underlying element (which will be applied to every element in the range).</li>
<li>A <code class="sourceCode cpp">tuple_formatter<span class="op">&lt;</span>Ts<span class="op">...&gt;</span></code> that uses a <code class="sourceCode cpp">formatter<span class="op">&lt;</span>T<span class="op">&gt;</span></code> for each <code class="sourceCode cpp">T</code> in <code class="sourceCode cpp">Ts<span class="op">...</span></code> to <code class="sourceCode cpp">parse</code> and <code class="sourceCode cpp">format</code> either a <code class="sourceCode cpp">pair</code>, <code class="sourceCode cpp">tuple</code>, or <code class="sourceCode cpp">array</code> with appropriate elements. This can accepted a specifier on the tuple-like (align/pad/width) as well as a specifier for each underlying element (with a custom delimiter).</li>
<li>A <code class="sourceCode cpp">retargeted_format_context</code> facility that allows the user to construct a new <code class="sourceCode cpp"><span class="op">(</span>w<span class="op">)</span>format_context</code> with a custom output iterator.</li>
<li>An <code class="sourceCode cpp">end_sentry</code> facility that allows the user to manipulate the parse context’s range, for generic parsing purposes.</li>
</ul>
<p>The standard library should add specializations of <code class="sourceCode cpp">formatter</code> for:</p>
<ul>
<li>any type <code class="sourceCode cpp">R</code> that is a <code class="sourceCode cpp">range</code> whose <code class="sourceCode cpp">reference</code> is <code class="sourceCode cpp">formattable</code>, which inherits from <code class="sourceCode cpp">range_formatter<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>ranges<span class="op">::</span>range_reference_t<span class="op">&lt;</span>R<span class="op">&gt;&gt;&gt;</span></code></li>
<li><code class="sourceCode cpp">pair<span class="op">&lt;</span>T, U<span class="op">&gt;</span></code> if <code class="sourceCode cpp">T</code> and <code class="sourceCode cpp">U</code> are <code class="sourceCode cpp">formattable</code>, which inherits from <code class="sourceCode cpp">tuple_formatter<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>T<span class="op">&gt;</span>, remove_cvref_t<span class="op">&lt;</span>U<span class="op">&gt;&gt;</span></code></li>
<li><code class="sourceCode cpp">tuple<span class="op">&lt;</span>Ts<span class="op">...&gt;</span></code> if all of <code class="sourceCode cpp">Ts<span class="op">...</span></code> are <code class="sourceCode cpp">formattable</code>, which inherits from <code class="sourceCode cpp">tuple_formatter<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>Ts<span class="op">&gt;...&gt;</span></code></li>
</ul>
<p>Additionally, the standard library should provide the following more specific specializations of <code class="sourceCode cpp">formatter</code>:</p>
<ul>
<li><code class="sourceCode cpp">vector<span class="op">&lt;</span><span class="dt">bool</span>, Alloc<span class="op">&gt;</span></code> (which formats as a range of <code class="sourceCode cpp"><span class="dt">bool</span></code>)</li>
<li>all the associative maps (<code class="sourceCode cpp">map</code>, <code class="sourceCode cpp">multimap</code>, <code class="sourceCode cpp">unordered_map</code>, <code class="sourceCode cpp">unordered_multimap</code>) if their respective key/value types are <code class="sourceCode cpp">formattable</code>. This accepts the same set of specifiers as any other range, except by <em>default</em> it will format as <code class="sourceCode cpp"><span class="op">{</span>k<span class="op">:</span> v, k<span class="op">:</span> v<span class="op">}</span></code> instead of <code class="sourceCode cpp"><span class="op">[(</span>k, v<span class="op">)</span>, <span class="op">(</span>k, v<span class="op">)]</span></code></li>
<li>all the associative sets (<code class="sourceCode cpp">sets</code>, <code class="sourceCode cpp">multiset</code>, <code class="sourceCode cpp">unordered_set</code>, <code class="sourceCode cpp">unordered_multiset</code>) if their respective key/value types are <code class="sourceCode cpp">formattable</code>. This accepts the same set of specifiers as any other range, except by <em>default</em> it will format as <code class="sourceCode cpp"><span class="op">{</span>v1, v2<span class="op">}</span></code> instead of <code class="sourceCode cpp"><span class="op">[</span>v1, v2<span class="op">]</span></code></li>
</ul>
<p>Formatting for <code class="sourceCode cpp">string</code>, <code class="sourceCode cpp">string_view</code>, and <code class="sourceCode cpp"><span class="dt">char</span></code>/<code class="sourceCode cpp"><span class="dt">wchar_t</span></code> will gain a <code class="sourceCode cpp"><span class="op">?</span></code> specifier, which causes these types to be printed as escaped and quoted if provided. Ranges and tuples will, by default, print their elements as escaped and quoted, unless the user provides a specifier for the element.</p>
<p>The standard library should also add a utility <code class="sourceCode cpp">std<span class="op">::</span>format_join</code> (or any other suitable name, knowing that <code class="sourceCode cpp">std<span class="op">::</span>views<span class="op">::</span>join</code> already exists), following in the footsteps of <code class="sourceCode cpp">fmt<span class="op">::</span>join</code>, which allows the user to provide more customization in how ranges and tuples get formatted. Even though this paper allows you to provide a specifier for each element in the range, it does not let you change the delimiter in the specifier (that’s… a bit much), so <code class="sourceCode cpp">fmt<span class="op">::</span>join</code> is still a useful and necessary facility for that.</p>
<h2 data-number="4.1" id="wording"><span class="header-section-number">4.1</span> Wording<a href="#wording" class="self-link"></a></h2>
<p>None yet, since spent all my time on implementation but nevertheless wanted to get this paper out sooner.</p>
<h1 data-number="5" style="border-bottom:1px solid #cccccc" id="bibliography"><span class="header-section-number">5</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references hanging-indent" role="doc-bibliography">
<div id="ref-LWG3478">
<p>[LWG3478] Barry Revzin. views::split drops trailing empty range. <br />
<a href="https://wg21.link/lwg3478">https://wg21.link/lwg3478</a></p>
</div>
<div id="ref-P2214R0">
<p>[P2214R0] Barry Revzin, Conor Hoekstra, Tim Song. 2020-10-15. A Plan for C++23 Ranges. <br />
<a href="https://wg21.link/p2214r0">https://wg21.link/p2214r0</a></p>
</div>
<div id="ref-P2216R3">
<p>[P2216R3] Victor Zverovich. 2021-02-15. std::format improvements. <br />
<a href="https://wg21.link/p2216r3">https://wg21.link/p2216r3</a></p>
</div>
<div id="ref-P2286R0">
<p>[P2286R0] Barry Revzin. 2021-01-15. Formatting Ranges. <br />
<a href="https://wg21.link/p2286r0">https://wg21.link/p2286r0</a></p>
</div>
<div id="ref-P2286R1">
<p>[P2286R1] Barry Revzin. 2021-02-19. Formatting Ranges. <br />
<a href="https://wg21.link/p2286r1">https://wg21.link/p2286r1</a></p>
</div>
<div id="ref-P2286R2">
<p>[P2286R2] Barry Revzin. Formatting Ranges. <br />
<a href="https://wg21.link/p2286r2">https://wg21.link/p2286r2</a></p>
</div>
<div id="ref-P2321R2">
<p>[P2321R2] Tim Song. 2021-06-11. zip. <br />
<a href="https://wg21.link/p2321r2">https://wg21.link/p2321r2</a></p>
</div>
<div id="ref-P2418R0">
<p>[P2418R0] Victor Zverovich. Add support for <code class="sourceCode cpp">std<span class="op">::</span>generator</code>-like types to <code class="sourceCode cpp">std<span class="op">::</span>format</code>. <br />
<a href="https://wg21.link/p2418r0">https://wg21.link/p2418r0</a></p>
</div>
<div id="ref-P2418R2">
<p>[P2418R2] Victor Zverovich. 2021-09-24. Add support for std::generator-like types to std::format. <br />
<a href="https://wg21.link/p2418r2">https://wg21.link/p2418r2</a></p>
</div>
</div>
</div>
</div>
</body>
</html>
