<!doctype html><html lang="en">
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
  <title>P3107R5: Permit an efficient implementation of std::print</title>
<style data-fill-with="stylesheet">/******************************************************************************
 *                   Style sheet for the W3C specifications                   *
 *
 * Special classes handled by this style sheet include:
 *
 * Indices
 *   - .toc for the Table of Contents (<ol class="toc">)
 *     + <span class="secno"> for the section numbers
 *   - #toc for the Table of Contents (<nav id="toc">)
 *   - ul.index for Indices (<a href="#ref">term</a><span>, in § N.M</span>)
 *   - table.index for Index Tables (e.g. for properties or elements)
 *
 * Structural Markup
 *   - table.data for general data tables
 *     -> use 'scope' attribute, <colgroup>, <thead>, and <tbody> for best results !
 *     -> use <table class='complex data'> for extra-complex tables
 *     -> use <td class='long'> for paragraph-length cell content
 *     -> use <td class='pre'> when manual line breaks/indentation would help readability
 *   - dl.switch for switch statements
 *   - ol.algorithm for algorithms (helps to visualize nesting)
 *   - .figure and .caption (HTML4) and figure and figcaption (HTML5)
 *     -> .sidefigure for right-floated figures
 *   - ins/del
 *     -> ins/del.c### for candidate and proposed changes (amendments)
 *
 * Code
 *   - pre and code
 *
 * Special Sections
 *   - .note       for informative notes             (div, p, span, aside, details)
 *   - .example    for informative examples          (div, p, pre, span)
 *   - .issue      for issues                        (div, p, span)
 *   - .advisement for loud normative statements     (div, p, strong)
 *   - .annoying-warning for spec obsoletion notices (div, aside, details)
 *   - .correction for "candidate corrections"       (div, aside, details, section)
 *   - .addition   for "candidate additions"         (div, aside, details, section)
 *   - .correction.proposed for "proposed corrections" (div, aside, details, section)
 *   - .addition.proposed   for "proposed additions"   (div, aside, details, section)
 *
 * Definition Boxes
 *   - pre.def   for WebIDL definitions
 *   - table.def for tables that define other entities (e.g. CSS properties)
 *   - dl.def    for definition lists that define other entitles (e.g. HTML elements)
 *
 * Numbering
 *   - .secno for section numbers in .toc and headings (<span class='secno'>3.2</span>)
 *   - .marker for source-inserted example/figure/issue numbers (<span class='marker'>Issue 4</span>)
 *   - ::before styled for CSS-generated issue/example/figure numbers:
 *     -> Documents wishing to use this only need to add
 *        figcaption::before,
 *        .caption::before { content: "Figure "  counter(figure) " ";  }
 *        .example::before { content: "Example " counter(example) " "; }
 *        .issue::before   { content: "Issue "   counter(issue) " ";   }
 *
 * Header Stuff (ignore, just don't conflict with these classes)
 *   - .head for the header
 *   - .copyright for the copyright
 *
 * Outdated warning for old specs
 *
 * Miscellaneous
 *   - .overlarge for things that should be as wide as possible, even if
 *     that overflows the body text area. This can be used on an item or
 *     on its container, depending on the effect desired.
 *     Note that this styling basically doesn't help at all when printing,
 *     since A4 paper isn't much wider than the max-width here.
 *     It's better to design things to fit into a narrower measure if possible.
 *
 *   - js-added ToC jump links (see fixup.js)
 *
 ******************************************************************************/

/* color variables included separately for reliability */

/******************************************************************************/
/*                                    Body                                    */
/******************************************************************************/

	html {
	}

	body {
		counter-reset: example figure issue;

		/* Layout */
		max-width: 50em;			  /* limit line length to 50em for readability   */
		margin: 0 auto;				/* center text within page                    */
		padding: 1.6em 1.5em 2em 50px; /* assume 16px font size for downlevel clients */
		padding: 1.6em 1.5em 2em calc(26px + 1.5em); /* leave space for status flag    */

		/* Typography */
		line-height: 1.5;
		font-family: sans-serif;
		widows: 2;
		orphans: 2;
		word-wrap: break-word;
		overflow-wrap: break-word;
		hyphens: auto;

		color: black;
		color: var(--text);
		background: white top left fixed no-repeat;
		background: var(--bg) top left fixed no-repeat;
		background-size: 25px auto;
	}


/******************************************************************************/
/*                         Front Matter & Navigation                          */
/******************************************************************************/

/** Header ********************************************************************/

	div.head { margin-bottom: 1em; }
	div.head hr { border-style: solid; }

	div.head h1 {
		font-weight: bold;
		margin: 0 0 .1em;
		font-size: 220%;
	}

	div.head h2 { margin-bottom: 1.5em;}

/** W3C Logo ******************************************************************/

	.head .logo {
		float: right;
		margin: 0.4rem 0 0.2rem .4rem;
	}

	.head img[src*="logos/W3C"] {
		display: block;
		border: solid #1a5e9a;
		border: solid var(--logo-bg);
		border-width: .65rem .7rem .6rem;
		border-radius: .4rem;
		background: #1a5e9a;
		background: var(--logo-bg);
		color: white;
		color: var(--logo-text);
		font-weight: bold;
	}

	.head a:hover > img[src*="logos/W3C"],
	.head a:focus > img[src*="logos/W3C"] {
		opacity: .8;
	}

	.head a:active > img[src*="logos/W3C"] {
		background: #c00;
		background: var(--logo-active-bg);
		border-color: #c00;
		border-color: var(--logo-active-bg);
	}

	/* see also additional rules in Link Styling section */

/** Copyright *****************************************************************/

	p.copyright,
	p.copyright small { font-size: small; }

/** Back to Top / ToC Toggle **************************************************/

	@media print {
		#toc-nav {
			display: none;
		}
	}
	@media not print {
		#toc-nav {
			position: fixed;
			z-index: 3;
			bottom: 0; left: 0;
			margin: 0;
			min-width: 1.33em;
			border-top-right-radius: 2rem;
			box-shadow: 0 0 2px;
			font-size: 1.5em;
		}
		#toc-nav > a {
			display: block;
			white-space: nowrap;

			height: 1.33em;
			padding: .1em 0.3em;
			margin: 0;

			box-shadow: 0 0 2px;
			border: none;
			border-top-right-radius: 1.33em;

			color: #707070;
			color: var(--tocnav-normal-text);
			background: white;
			background: var(--tocnav-normal-bg);
		}
		#toc-nav > a:hover,
		#toc-nav > a:focus {
			color: black;
			color: var(--tocnav-hover-text);
			background: #f8f8f8;
			background: var(--tocnav-hover-bg);
		}
		#toc-nav > a:active {
			color: #c00;
			color: var(--tocnav-active-text);
			background: white;
			background: var(--tocnav-active-bg);
		}

		#toc-nav > #toc-jump {
			padding-bottom: 2em;
			margin-bottom: -1.9em;
		}

		/* statusbar gets in the way on keyboard focus; remove once browsers fix */
		#toc-nav > a[href="#toc"]:not(:hover):focus:last-child {
			padding-bottom: 1.5rem;
		}

		#toc-nav:not(:hover) > a:not(:focus) > span + span {
			/* Ideally this uses :focus-within on #toc-nav */
			display: none;
		}
		#toc-nav > a > span + span {
			padding-right: 0.2em;
		}
	}

/** ToC Sidebar ***************************************************************/

	/* Floating sidebar */
	@media screen {
		body.toc-sidebar #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			max-width: 80%;
			max-width: calc(100% - 2em - 26px);
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body.toc-sidebar #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}
		body.toc-sidebar #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	/* Hide main scroller when only the ToC is visible anyway */
	@media screen and (max-width: 28em) {
		body.toc-sidebar {
			overflow: hidden;
		}
	}

	/* Sidebar with its own space */
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body:not(.toc-inline) #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}

		body:not(.toc-inline) {
			padding-left: 29em;
		}
		/* See also Overflow section at the bottom */

		body:not(.toc-inline) #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) {
			margin: 0 4em;
		}
	}

/******************************************************************************/
/*                                Sectioning                                  */
/******************************************************************************/

/** Headings ******************************************************************/

	h1, h2, h3, h4, h5, h6, dt {
		page-break-after: avoid;
		page-break-inside: avoid;
		font: 100% sans-serif;   /* Reset all font styling to clear out UA styles */
		font-family: inherit;	/* Inherit the font family. */
		line-height: 1.2;		/* Keep wrapped headings compact */
		hyphens: manual;		/* Hyphenated headings look weird */
	}

	h2, h3, h4, h5, h6 {
		margin-top: 3rem;
	}

	h1, h2, h3 {
		color: #005A9C;
		color: var(--heading-text);
	}

	h1 { font-size: 170%; }
	h2 { font-size: 140%; }
	h3 { font-size: 120%; }
	h4 { font-weight: bold; }
	h5 { font-style: italic; }
	h6 { font-variant: small-caps; }
	dt { font-weight: bold; }

/** Subheadings ***************************************************************/

	h1 + h2,
	#profile-and-date {
		/* #profile-and-date is a subtitle in an H2 under the H1 */
		margin-top: 0;
	}
	h2 + h3,
	h3 + h4,
	h4 + h5,
	h5 + h6 {
		margin-top: 1.2em; /* = 1 x line-height */
	}

/** Section divider ***********************************************************/

	:not(.head) > :not(.head) + hr {
		font-size: 1.5em;
		text-align: center;
		margin: 1em auto;
		height: auto;
		color: black;
		color: var(--hr-text);
		border: transparent solid 0;
		background: transparent;
	}
	:not(.head) > hr::before {
		content: "\2727\2003\2003\2727\2003\2003\2727";
	}

/******************************************************************************/
/*                            Paragraphs and Lists                            */
/******************************************************************************/

	p {
		margin: 1em 0;
	}

	dd > p:first-child,
	li > p:first-child {
		margin-top: 0;
	}

	ul, ol {
		margin-left: 0;
		padding-left: 2em;
	}

	li {
		margin: 0.25em 0 0.5em;
		padding: 0;
	}

	dl dd {
		margin: 0 0 .5em 2em;
	}

	.head dd + dd { /* compact for header */
		margin-top: -.5em;
	}

	/* Style for algorithms */
	ol.algorithm ol:not(.algorithm),
	.algorithm > ol ol:not(.algorithm) {
	border-left: 0.5em solid #DEF;
	border-left: 0.5em solid var(--algo-border);
	}

	/* Put nice boxes around each algorithm. */
	[data-algorithm]:not(.heading) {
	 padding: .5em;
	 border: thin solid #ddd;
	 border: thin solid var(--algo-border);
	 border-radius: .5em;
	 margin: .5em calc(-0.5em - 1px);
	}
	[data-algorithm]:not(.heading) > :first-child {
	 margin-top: 0;
	}
	[data-algorithm]:not(.heading) > :last-child {
	 margin-bottom: 0;
	}

	/* Style for switch/case <dl>s */
	dl.switch > dd > ol.only,
	dl.switch > dd > .only > ol {
	margin-left: 0;
	}
	dl.switch > dd > ol.algorithm,
	dl.switch > dd > .algorithm > ol {
	margin-left: -2em;
	}
	dl.switch {
	padding-left: 2em;
	}
	dl.switch > dt {
	text-indent: -1.5em;
	margin-top: 1em;
	}
	dl.switch > dt + dt {
	margin-top: 0;
	}
	dl.switch > dt::before {
	content: '\21AA';
	padding: 0 0.5em 0 0;
	display: inline-block;
	width: 1em;
	text-align: right;
	line-height: 0.5em;
	}

/** Terminology Markup ********************************************************/


/******************************************************************************/
/*                                 Inline Markup                              */
/******************************************************************************/

/** Terminology Markup ********************************************************/
	dfn   { /* Defining instance */
		font-weight: bolder;
	}
	a > i { /* Instance of term */
		font-style: normal;
	}
	dt dfn code, code.idl {
		font-size: inherit;
	}
	dfn var {
		font-style: normal;
	}

/** Change Marking ************************************************************/

	del {
		color: #aa0000;
		color: var(--del-text);
		background: transparent;
		background: var(--del-bg);
		text-decoration: line-through;
	}
	ins {
		color: #006100;
		color: var(--ins-text);
		background: transparent;
		background: var(--ins-bg);
		text-decoration: underline;
	}

	/* for amendments (candidate/proposed changes) */

	.amendment ins, .correction ins, .addition ins,
	ins[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment del, .correction del, .addition del,
	del[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment.proposed ins, .correction.proposed ins, .addition.proposed ins,
	ins[class^=c].proposed {
		text-decoration-style: double;
	}
	.amendment.proposed del, .correction.proposed del, .addition.proposed del,
	del[class^=c].proposed {
		text-decoration-style: double;
	}

/** Miscellaneous improvements to inline formatting ***************************/

	sup {
		vertical-align: super;
		font-size: 80%
	}

/******************************************************************************/
/*                                    Code                                    */
/******************************************************************************/

/** General monospace/pre rules ***********************************************/

	pre, code, samp {
		font-family: Menlo, Consolas, "DejaVu Sans Mono", Monaco, monospace;
		font-size: .9em;
		hyphens: none;
		text-transform: none;
		text-align: left;
		text-align: start;
		font-variant: normal;
		orphans: 3;
		widows: 3;
		page-break-before: avoid;
	}
	pre code,
	code code {
		font-size: 100%;
	}

	pre {
		margin-top: 1em;
		margin-bottom: 1em;
		overflow: auto;
	}

/** Inline Code fragments *****************************************************/

	/* Do something nice. */

/******************************************************************************/
/*                                    Links                                   */
/******************************************************************************/

/** General Hyperlinks ********************************************************/

	/* We hyperlink a lot, so make it less intrusive */
	a[href] {
		color: #034575;
		color: var(--a-normal-text);
		text-decoration: underline #707070;
		text-decoration: underline var(--a-normal-underline);
		text-decoration-skip-ink: none;
	}
	a:visited {
		color: #034575;
		color: var(--a-visited-text);
		text-decoration-color: #bbb;
		text-decoration-color: var(--a-visited-underline);
	}

	/* Indicate interaction with the link */
	a[href]:focus,
	a[href]:hover {
		text-decoration-thickness: 2px;
	}
	a[href]:active {
		color: #c00;
		color: var(--a-active-text);
		text-decoration-color: #c00;
		text-decoration-color: var(--a-active-underline);
	}

	/* Backout above styling for W3C logo */
	.head .logo,
	.head .logo a {
		border: none;
		text-decoration: none;
		background: transparent;
	}

/******************************************************************************/
/*                                    Images                                  */
/******************************************************************************/

	img {
		border-style: none;
	}

	img, svg {
		/* Intentionally not color-scheme aware. */
		background: white;
	}

	/* For autogen numbers, add
	  .caption::before, figcaption::before { content: "Figure " counter(figure) ". "; }
	*/

	figure, .figure, .sidefigure {
		page-break-inside: avoid;
		text-align: center;
		margin: 2.5em 0;
	}
	.figure img,	.sidefigure img,	figure img,
	.figure object, .sidefigure object, figure object {
		max-width: 100%;
		margin: auto;
		height: auto;
	}
	.figure pre, .sidefigure pre, figure pre {
		text-align: left;
		display: table;
		margin: 1em auto;
	}
	.figure table, figure table {
		margin: auto;
	}
	@media screen and (min-width: 20em) {
		.sidefigure {
			float: right;
			width: 50%;
			margin: 0 0 0.5em 0.5em;
		}
	}
	.caption, figcaption, caption {
		font-style: italic;
		font-size: 90%;
	}
	.caption::before, figcaption::before, figcaption > .marker {
		font-weight: bold;
	}
	.caption, figcaption {
		counter-increment: figure;
	}

	/* DL list is indented 2em, but figure inside it is not */
	dd > .figure, dd > figure { margin-left: -2em; }

/******************************************************************************/
/*                             Colored Boxes                                  */
/******************************************************************************/

	.issue, .note, .example, .assertion, .advisement, blockquote,
	.amendment, .correction, .addition {
		margin: 1em auto;
		padding: .5em;
		border: .5em;
		border-left-style: solid;
		page-break-inside: avoid;
	}
	span.issue, span.note {
		padding: .1em .5em .15em;
		border-right-style: solid;
	}

	blockquote > :first-child,
	.note  > p:first-child,
	.issue > p:first-child,
	.amendment > p:first-child,
	.correction > p:first-child,
	.addition > p:first-child {
		margin-top: 0;
	}
	blockquote > :last-child,
	.note  > p:last-child,
	.issue > p:last-child,
	.amendment > p:last-child,
	.correction > p:last-child,
	.addition > p:last-child {
		margin-bottom: 0;
	}


	.issue::before, .issue > .marker,
	.example::before, .example > .marker,
	.note::before, .note > .marker,
	details.note > summary > .marker,
	.amendment::before, .amendment > .marker,
	details.amendment > summary > .marker,
	.addition::before, .addition > .marker,
	addition.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	correction.amendment > summary > .marker
	{
		text-transform: uppercase;
		padding-right: 1em;
	}

	.example::before, .example > .marker {
		display: block;
		padding-right: 0em;
	}

/** Blockquotes ***************************************************************/

	blockquote {
		border-color: silver;
		border-color: var(--blockquote-border);
		background: transparent;
		background: var(--blockquote-bg);
		color: currentcolor;
		color: var(--blockquote-text);
	}

/** Open issue ****************************************************************/

	.issue {
		border-color: #e05252;
		border-color: var(--issue-border);
		background: #fbe9e9;
		background: var(--issue-bg);
		color: black;
		color: var(--issue-text);
		counter-increment: issue;
		overflow: auto;
	}
	.issue::before, .issue > .marker {
		color: #831616;
		color: var(--issueheading-text);
	}
	/* Add .issue::before { content: "Issue " counter(issue) " "; } for autogen numbers,
	  or use class="marker" to mark up the issue number in source. */

/** Example *******************************************************************/

	.example {
		border-color: #e0cb52;
		border-color: var(--example-border);
		background: #fcfaee;
		background: var(--example-bg);
		color: black;
		color: var(--example-text);
		counter-increment: example;
		overflow: auto;
		clear: both;
	}
	.example::before, .example > .marker {
		color: #574b0f;
		color: var(--exampleheading-text);
	}
	/* Add .example::before { content: "Example " counter(example) " "; } for autogen numbers,
	  or use class="marker" to mark up the example number in source. */

/** Non-normative Note ********************************************************/

	.note {
		border-color: #52e052;
		border-color: var(--note-border);
		background: #e9fbe9;
		background: var(--note-bg);
		color: black;
		color: var(--note-text);
		overflow: auto;
	}

	.note::before, .note > .marker,
	details.note > summary {
		color: hsl(120, 70%, 30%);
		color: var(--noteheading-text);
	}
	/* Add .note::before { content: "Note "; } for autogen label,
	  or use class="marker" to mark up the label in source. */

	details.note[open] > summary {
		border-bottom: 1px silver solid;
		border-bottom: 1px var(--notesummary-underline) solid;
	}

/** Assertion Box *************************************************************/
	/*  for assertions in algorithms */

	.assertion {
		border-color: #AAA;
		border-color: var(--assertion-border);
		background: #EEE;
		background: var(--assertion-bg);
		color: black;
		color: var(--assertion-text);
	}

/** Advisement Box ************************************************************/
	/*  for attention-grabbing normative statements */

	.advisement {
		border-color: orange;
		border-color: var(--advisement-border);
		border-style: none solid;
		background: #fec;
		background: var(--advisement-bg);
		color: black;
		color: var(--advisement-text);
	}
	strong.advisement {
		display: block;
		text-align: center;
	}
	.advisement::before, .advisement > .marker {
		color: #b35f00;
		color: var(--advisementheading-text);
	}

/** Amendment Box *************************************************************/

	.amendment, .correction, .addition {
		border-color: #330099;
		border-color: var(--amendment-border);
		background: #F5F0FF;
		background: var(--amendment-bg);
		color: black;
		color: var(--amendment-text);
	}
	.amendment.proposed, .correction.proposed, .addition.proposed {
		border-style: solid;
		border-block-width: 0.25em;
	}
	.amendment::before, .amendment > .marker,
	details.amendment > summary::before, details.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	details.correction > summary::before, details.correction > summary > .marker,
	.addition::before, .addition > .marker,
	details.addition > summary::before, details.addition > summary > .marker {
		color: #220066;
		color: var(--amendmentheading-text);
	}
	.amendment.proposed::before, .amendment.proposed > .marker,
	details.amendment.proposed > summary::before, details.amendment.proposed > summary > .marker,
	.correction.proposed::before, .correction.proposed > .marker,
	details.correction.proposed > summary::before, details.correction.proposed > summary > .marker,
	.addition.proposed::before, .addition.proposed > .marker,
	details.addition.proposed > summary::before, details.addition.proposed > summary > .marker {
		font-weight: bold;
	}

/** Spec Obsoletion Notice ****************************************************/
	/* obnoxious obsoletion notice for older/abandoned specs. */

	details {
		display: block;
	}
	summary {
		font-weight: bolder;
	}

	.annoying-warning:not(details),
	details.annoying-warning:not([open]) > summary,
	details.annoying-warning[open] {
		background: hsla(40,100%,50%,0.95);
		background: var(--warning-bg);
		color: black;
		color: var(--warning-text);
		padding: .75em 1em;
		border: red;
		border: var(--warning-border);
		border-style: solid none;
		box-shadow: 0 2px 8px black;
		text-align: center;
	}
	.annoying-warning :last-child {
		margin-bottom: 0;
	}

@media not print {
	details.annoying-warning[open] {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 2em;
		z-index: 1000;
	}
}

	details.annoying-warning:not([open]) > summary {
		text-align: center;
	}

/** Entity Definition Boxes ***************************************************/

	.def {
		padding: .5em 1em;
		background: #def;
		background: var(--def-bg);
		margin: 1.2em 0;
		border-left: 0.5em solid #8ccbf2;
		border-left: 0.5em solid var(--def-border);
		color: black;
		color: var(--def-text);
	}

/******************************************************************************/
/*                                    Tables                                  */
/******************************************************************************/

	th, td {
		text-align: left;
		text-align: start;
	}

/** Property/Descriptor Definition Tables *************************************/

	table.def {
		/* inherits .def box styling, see above */
		width: 100%;
		border-spacing: 0;
	}

	table.def td,
	table.def th {
		padding: 0.5em;
		vertical-align: baseline;
		border-bottom: 1px solid #bbd7e9;
		border-bottom: 1px solid var(--defrow-border);
	}

	table.def > tbody > tr:last-child th,
	table.def > tbody > tr:last-child td {
		border-bottom: 0;
	}

	table.def th {
		font-style: italic;
		font-weight: normal;
		padding-left: 1em;
		width: 3em;
	}

	/* For when values are extra-complex and need formatting for readability */
	table td.pre {
		white-space: pre-wrap;
	}

	/* A footnote at the bottom of a def table */
	table.def td.footnote {
		padding-top: 0.6em;
	}
	table.def td.footnote::before {
		content: " ";
		display: block;
		height: 0.6em;
		width: 4em;
		border-top: thin solid;
	}

/** Data tables (and properly marked-up index tables) *************************/
	/*
		<table class="data"> highlights structural relationships in a table
		when correct markup is used (e.g. thead/tbody, th vs. td, scope attribute)

		Use class="complex data" for particularly complicated tables --
		(This will draw more lines: busier, but clearer.)

		Use class="long" on table cells with paragraph-like contents
		(This will adjust text alignment accordingly.)
		Alternately use class="longlastcol" on tables, to have the last column assume "long".
	*/

	table {
		word-wrap: normal;
		overflow-wrap: normal;
		hyphens: manual;
	}

	table.data,
	table.index {
		margin: 1em auto;
		border-collapse: collapse;
		border: hidden;
		width: 100%;
	}
	table.data caption,
	table.index caption {
		max-width: 50em;
		margin: 0 auto 1em;
	}

	table.data td,  table.data th,
	table.index td, table.index th {
		padding: 0.5em 1em;
		border-width: 1px;
		border-color: silver;
		border-color: var(--datacell-border);
		border-top-style: solid;
	}

	table.data thead td:empty {
		padding: 0;
		border: 0;
	}

	table.data  thead,
	table.index thead,
	table.data  tbody,
	table.index tbody {
		border-bottom: 2px solid;
	}

	table.data colgroup,
	table.index colgroup {
		border-left: 2px solid;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		border-right: 2px solid;
		border-top: 1px solid silver;
		border-top: 1px solid var(--datacell-border);
		padding-right: 1em;
	}

	table.data th[colspan],
	table.data td[colspan] {
		text-align: center;
	}

	table.complex.data th,
	table.complex.data td {
		border: 1px solid silver;
		border: 1px solid var(--datacell-border);
		text-align: center;
	}

	table.data.longlastcol td:last-child,
	table.data td.long {
		vertical-align: baseline;
		text-align: left;
	}

	table.data img {
		vertical-align: middle;
	}


/*
Alternate table alignment rules

	table.data,
	table.index {
		text-align: center;
	}

	table.data  thead th[scope="row"],
	table.index thead th[scope="row"] {
		text-align: right;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		text-align: right;
	}

Possible extra rowspan handling

	table.data  tbody th[rowspan]:not([rowspan='1']),
	table.index tbody th[rowspan]:not([rowspan='1']),
	table.data  tbody td[rowspan]:not([rowspan='1']),
	table.index tbody td[rowspan]:not([rowspan='1']) {
		border-left: 1px solid silver;
	}

	table.data  tbody th[rowspan]:first-child,
	table.index tbody th[rowspan]:first-child,
	table.data  tbody td[rowspan]:first-child,
	table.index tbody td[rowspan]:first-child{
		border-left: 0;
		border-right: 1px solid silver;
	}
*/

/******************************************************************************/
/*                                  Indices                                   */
/******************************************************************************/


/** Table of Contents *********************************************************/

	.toc a {
		/* More spacing; use padding to make it part of the click target. */
		padding: 0.1rem 1px 0;
		/* Larger, more consistently-sized click target */
		display: block;
		/* Switch to using border-bottom for underlines */
		text-decoration: none;
		border-bottom: 1px solid;
		/* Reverse color scheme */
		color: black;
		color: var(--toclink-text);
		border-color: #3980b5;
		border-color: var(--toclink-underline);
	}
	.toc a:visited {
		color: black;
		color: var(--toclink-visited-text);
		border-color: #054572;
		border-color: var(--toclink-visited-underline);
	}
	.toc a:focus,
	.toc a:hover {
		background: rgba(75%, 75%, 75%, .25);
		background: var(--a-hover-bg);
		border-bottom-width: 3px;
		margin-bottom: -2px;
	}
	.toc a:not(:focus):not(:hover) {
		/* Allow colors to cascade through from link styling */
		border-bottom-color: transparent;
	}

	.toc, .toc ol, .toc ul, .toc li {
		list-style: none; /* Numbers must be inlined into source */
		/* because generated content isn't search/selectable and markers can't do multilevel yet */
		margin:  0;
		padding: 0;
	}
	.toc {
		line-height: 1.1em;
	}

	/* ToC not indented until third level, but font style & margins show hierarchy */
	.toc > li			{ font-weight: bold;   }
	.toc > li li		 { font-weight: normal; }
	.toc > li li li	  { font-size:   95%;	}
	.toc > li li li li	{ font-size:   90%;	}
	.toc > li li li li li { font-size:   85%;	}

	/* @supports not (display:grid) { */
		.toc > li			{ margin: 1.5rem 0;	}
		.toc > li li		 { margin: 0.3rem 0;	}
		.toc > li li li	  { margin-left: 2rem;   }

		/* Section numbers in a column of their own */
		.toc .secno {
			float: left;
			width: 4rem;
			white-space: nowrap;
		}
		.toc > li li li li .secno { font-size: 85%; }
		.toc > li li li li li .secno { font-size: 100%; }

		.toc li {
			clear: both;
		}

		:not(li) > .toc			 { margin-left:  5rem; }
		.toc .secno				 { margin-left: -5rem; }
		.toc > li li li .secno	  { margin-left: -7rem; }
		.toc > li li li li .secno	{ margin-left: -9rem; }
		.toc > li li li li li .secno { margin-left: -11rem; }

		/* Tighten up indentation in narrow ToCs */
		@media (max-width: 30em) {
			:not(li) > .toc			 { margin-left:  4rem; }
			.toc .secno				 { margin-left: -4rem; }
			.toc > li li li			 { margin-left:  1rem; }
			.toc > li li li .secno	  { margin-left: -5rem; }
			.toc > li li li li .secno	{ margin-left: -6rem; }
			.toc > li li li li li .secno { margin-left: -7rem; }
		}
		/* Loosen it on wide screens */
		@media screen and (min-width: 78em) {
			body:not(.toc-inline) :not(li) > .toc			 { margin-left:  4rem; }
			body:not(.toc-inline) .toc .secno				 { margin-left: -4rem; }
			body:not(.toc-inline) .toc > li li li			 { margin-left:  1rem; }
			body:not(.toc-inline) .toc > li li li .secno	  { margin-left: -5rem; }
			body:not(.toc-inline) .toc > li li li li .secno	{ margin-left: -6rem; }
			body:not(.toc-inline) .toc > li li li li li .secno { margin-left: -7rem; }
	}
	/* } */

	@supports (display:grid) and (display:contents) {
		/* Use #toc over .toc to override non-@supports rules. */
		#toc {
			display: grid;
			align-content: start;
			grid-template-columns: auto 1fr;
			grid-column-gap: 1rem;
			column-gap: 1rem;
			grid-row-gap: .6rem;
			row-gap: .6rem;
		}
		#toc h2 {
			grid-column: 1 / -1;
			margin-bottom: 0;
		}
		#toc ol,
		#toc li,
		#toc a {
			display: contents;
			/* Switch <a> to subgrid when supported */
		}
		#toc span {
			margin: 0;
		}
		#toc > .toc > li > a > span {
			/* The spans of the top-level list,
			  comprising the first items of each top-level section. */
			margin-top: 1.1rem;
		}
		#toc#toc .secno { /* Ugh, need more specificity to override base.css */
			grid-column: 1;
			width: auto;
			margin-left: 0;
		}
		#toc .content {
			grid-column: 2;
			width: auto;
			margin-right: 1rem;
			border-bottom: 3px solid transparent;
			margin-bottom: -3px;
		}
		#toc .content:hover,
		#toc .content:focus {
			background: rgba(75%, 75%, 75%, .25);
			background: var(--a-hover-bg);
			border-bottom-color: #054572;
			border-bottom-color: var(--toclink-underline);
		}
		#toc li li li .content {
			margin-left: 1rem;
		}
		#toc li li li li .content {
			margin-left: 2rem;
		}
	}


/** Index *********************************************************************/

	/* Index Lists: Layout */
	ul.index	  { margin-left: 0; columns: 15em; text-indent: 1em hanging; }
	ul.index li	{ margin-left: 0; list-style: none; break-inside: avoid; }
	ul.index li li { margin-left: 1em; }
	ul.index dl	{ margin-top: 0; }
	ul.index dt	{ margin: .2em 0 .2em 20px;}
	ul.index dd	{ margin: .2em 0 .2em 40px;}
	/* Index Lists: Typography */
	ul.index ul,
	ul.index dl { font-size: smaller; }
	@media not print {
		ul.index li a + span {
			white-space: nowrap;
			color: transparent; }
		ul.index li a:hover + span,
		ul.index li a:focus + span {
			color: #707070;
			color: var(--indexinfo-text);
		}
	}

/** Index Tables *****************************************************/
	/* See also the data table styling section, which this effectively subclasses */

	table.index {
		font-size: small;
		border-collapse: collapse;
		border-spacing: 0;
		text-align: left;
		margin: 1em 0;
	}

	table.index td,
	table.index th {
		padding: 0.4em;
	}

	table.index tr:hover td:not([rowspan]),
	table.index tr:hover th:not([rowspan]) {
		color: black;
		color: var(--indextable-hover-text);
		background: #f7f8f9;
		background: var(--indextable-hover-bg);
	}

	/* The link in the first column in the property table (formerly a TD) */
	table.index th:first-child a {
		font-weight: bold;
	}

/** Outdated warning **********************************************************/

.outdated-spec {
	color: black;
	color: var(--outdatedspec-text);
	background-color: rgba(0,0,0,0.5);
	background-color: var(--outdatedspec-bg);
}

.outdated-warning {
	position: fixed;
	bottom: 50%;
	left: 0;
	right: 0;
	margin: 0 auto;
	width: 50%;
	background: maroon;
	background: var(--outdated-bg);
	color: white;
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	position: absolute;
	top: 0;
	right:0;
	margin: 0;
	border: 0;
	padding: 0.25em 0.5em;
	background: transparent;
	color: white;
	color: var(--outdated-text);
	font:1em sans-serif;
	text-align:center;
}

.outdated-warning span {
	display: block;
}

.outdated-collapsed {
	bottom: 0;
	border-radius: 0;
	width: 100%;
	padding: 0;
}

/******************************************************************************/
/*                                    Print                                   */
/******************************************************************************/

	@media print {
		/* Pages have their own margins. */
		html {
			margin: 0;
		}
		/* Serif for print. */
		body {
			font-family: serif;
		}

		.outdated-warning {
			position: absolute;
			border-style: solid;
			border-color: red;
		}

		.outdated-warning input {
			display: none;
		}
	}
	@page {
		margin: 1.5cm 1.1cm;
	}



/******************************************************************************/
/*                             Overflow Control                               */
/******************************************************************************/

	.figure .caption, .sidefigure .caption, figcaption {
		/* in case figure is overlarge, limit caption to 50em */
		max-width: 50rem;
		margin-left: auto;
		margin-right: auto;
	}
	.overlarge {
		/* Magic to create good item positioning:
		  "content column" is 50ems wide at max; less on smaller screens.
		  Extra space (after ToC + content) is empty on the right.

		  1. When item < content column, centers item in column.
		  2. When content < item < available, left-aligns.
		  3. When item > available, fills available + scroll bar.
		*/
		display: grid;
		grid-template-columns: minmax(0, 50em);
	}
	.overlarge > table {
		/* limit preferred width of table */
		max-width: 50em;
		margin-left: auto;
		margin-right: auto;
	}

	@media (min-width: 55em) {
		.overlarge {
			margin-right: calc(13px + 26.5rem - 50vw);
			max-width: none;
		}
	}
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) .overlarge {
			/* 30.5em body padding 50em content area */
			margin-right: calc(40em - 50vw) !important;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) .overlarge {
			/* 4em html margin 30.5em body padding 50em content area */
			margin-right: calc(84.5em - 100vw) !important;
		}
	}

	@media not print {
		.overlarge {
			overflow-x: auto;
			/* See Lea Verou's explanation background-attachment:
			* http://lea.verou.me/2012/04/background-attachment-local/
			*
			background: top left  / 4em 100% linear-gradient(to right,  #ffffff, rgba(255, 255, 255, 0)) local,
						top right / 4em 100% linear-gradient(to left, #ffffff, rgba(255, 255, 255, 0)) local,
						top left  / 1em 100% linear-gradient(to right,  #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						top right / 1em 100% linear-gradient(to left, #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						white;
			background-repeat: no-repeat;
			*/
		}
	}
</style>
<style>
    table, th, td {
      border: 1px solid black;
      border-collapse: collapse;
      vertical-align: top;
    }
    th, td {
      border-left: none;
      border-right: none;
      padding: 0px 10px;
    }
    th {
      text-align: center;
    }

    del { background: #fcc; color: #000; text-decoration: line-through; }
    ins { background: #cfc; color: #000; }
    blockquote .highlight:not(.idl) { background: initial; margin: initial; padding: 0.5em }
    blockquote ul { background: inherit; }
    blockquote code.highlight:not(.idl) { padding: initial; }
    blockquote c-[a] { color: inherit; } /* Keyword.Declaration */
    blockquote c-[b] { color: inherit; } /* Keyword.Type */
    blockquote c-[c] { color: inherit; } /* Comment */
    blockquote c-[d] { color: inherit; } /* Comment.Multiline */
    blockquote c-[e] { color: inherit; } /* Name.Attribute */
    blockquote c-[f] { color: inherit; } /* Name.Tag */
    blockquote c-[g] { color: inherit; } /* Name.Variable */
    blockquote c-[k] { color: inherit; } /* Keyword */
    blockquote c-[l] { color: inherit; } /* Literal */
    blockquote c-[m] { color: inherit; } /* Literal.Number */
    blockquote c-[n] { color: inherit; } /* Name */
    blockquote c-[o] { color: inherit; } /* Operator */
    blockquote c-[p] { color: inherit; } /* Punctuation */
    blockquote c-[s] { color: inherit; } /* Literal.String */
    blockquote c-[t] { color: inherit; } /* Literal.String.Single */
    blockquote c-[u] { color: inherit; } /* Literal.String.Double */
    blockquote c-[cp] { color: inherit; } /* Comment.Preproc */
    blockquote c-[c1] { color: inherit; } /* Comment.Single */
    blockquote c-[cs] { color: inherit; } /* Comment.Special */
    blockquote c-[kc] { color: inherit; } /* Keyword.Constant */
    blockquote c-[kn] { color: inherit; } /* Keyword.Namespace */
    blockquote c-[kp] { color: inherit; } /* Keyword.Pseudo */
    blockquote c-[kr] { color: inherit; } /* Keyword.Reserved */
    blockquote c-[ld] { color: inherit; } /* Literal.Date */
    blockquote c-[nc] { color: inherit; } /* Name.Class */
    blockquote c-[no] { color: inherit; } /* Name.Constant */
    blockquote c-[nd] { color: inherit; } /* Name.Decorator */
    blockquote c-[ni] { color: inherit; } /* Name.Entity */
    blockquote c-[ne] { color: inherit; } /* Name.Exception */
    blockquote c-[nf] { color: inherit; } /* Name.Function */
    blockquote c-[nl] { color: inherit; } /* Name.Label */
    blockquote c-[nn] { color: inherit; } /* Name.Namespace */
    blockquote c-[py] { color: inherit; } /* Name.Property */
    blockquote c-[ow] { color: inherit; } /* Operator.Word */
    blockquote c-[mb] { color: inherit; } /* Literal.Number.Bin */
    blockquote c-[mf] { color: inherit; } /* Literal.Number.Float */
    blockquote c-[mh] { color: inherit; } /* Literal.Number.Hex */
    blockquote c-[mi] { color: inherit; } /* Literal.Number.Integer */
    blockquote c-[mo] { color: inherit; } /* Literal.Number.Oct */
    blockquote c-[sb] { color: inherit; } /* Literal.String.Backtick */
    blockquote c-[sc] { color: inherit; } /* Literal.String.Char */
    blockquote c-[sd] { color: inherit; } /* Literal.String.Doc */
    blockquote c-[se] { color: inherit; } /* Literal.String.Escape */
    blockquote c-[sh] { color: inherit; } /* Literal.String.Heredoc */
    blockquote c-[si] { color: inherit; } /* Literal.String.Interpol */
    blockquote c-[sx] { color: inherit; } /* Literal.String.Other */
    blockquote c-[sr] { color: inherit; } /* Literal.String.Regex */
    blockquote c-[ss] { color: inherit; } /* Literal.String.Symbol */
    blockquote c-[vc] { color: inherit; } /* Name.Variable.Class */
    blockquote c-[vg] { color: inherit; } /* Name.Variable.Global */
    blockquote c-[vi] { color: inherit; } /* Name.Variable.Instance */
    blockquote c-[il] { color: inherit; } /* Literal.Number.Integer.Long */
  </style>
  <meta content="Bikeshed version 4416b18d5, updated Tue Jan 2 15:52:39 2024 -0800" name="generator">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
  <meta content="7586f8adae2fde39413341e28627363ac0b8c331" name="revision">
<style>/* Boilerplate: style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
    color: var(--a-normal-text);
    font-size: inherit;
    font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
    content: "‘";
}
.css::after, .property::after, .descriptor::after {
    content: "’";
}
.property, .descriptor {
    /* Don't wrap property and descriptor names */
    white-space: nowrap;
}
.type { /* CSS value <type> */
    font-style: italic;
}
pre .property::before, pre .property::after {
    content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
    content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
    content: "’";
}

[data-link-type].production::before,
[data-link-type].production::after,
.prod [data-link-type]::before,
.prod [data-link-type]::after {
    content: "";
}

[data-link-type=element],
[data-link-type=element-attr] {
    font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
    font-size: .9em;
}
[data-link-type=element]::before { content: "<" }
[data-link-type=element]::after  { content: ">" }

[data-link-type=biblio] {
    white-space: pre;
}

@media (prefers-color-scheme: dark) {
    :root {
        --selflink-text: black;
        --selflink-bg: silver;
        --selflink-hover-text: white;
    }
}
</style>
<style>/* Boilerplate: style-colors */
/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
    color-scheme: light dark;

    --text: black;
    --bg: white;

    --unofficial-watermark: url(https://www.w3.org/StyleSheets/TR/2016/logos/UD-watermark);

    --logo-bg: #1a5e9a;
    --logo-active-bg: #c00;
    --logo-text: white;

    --tocnav-normal-text: #707070;
    --tocnav-normal-bg: var(--bg);
    --tocnav-hover-text: var(--tocnav-normal-text);
    --tocnav-hover-bg: #f8f8f8;
    --tocnav-active-text: #c00;
    --tocnav-active-bg: var(--tocnav-normal-bg);

    --tocsidebar-text: var(--text);
    --tocsidebar-bg: #f7f8f9;
    --tocsidebar-shadow: rgba(0,0,0,.1);
    --tocsidebar-heading-text: hsla(203,20%,40%,.7);

    --toclink-text: var(--text);
    --toclink-underline: #3980b5;
    --toclink-visited-text: var(--toclink-text);
    --toclink-visited-underline: #054572;

    --heading-text: #005a9c;

    --hr-text: var(--text);

    --algo-border: #def;

    --del-text: red;
    --del-bg: transparent;
    --ins-text: #080;
    --ins-bg: transparent;

    --a-normal-text: #034575;
    --a-normal-underline: #bbb;
    --a-visited-text: var(--a-normal-text);
    --a-visited-underline: #707070;
    --a-hover-bg: rgba(75%, 75%, 75%, .25);
    --a-active-text: #c00;
    --a-active-underline: #c00;

    --blockquote-border: silver;
    --blockquote-bg: transparent;
    --blockquote-text: currentcolor;

    --issue-border: #e05252;
    --issue-bg: #fbe9e9;
    --issue-text: var(--text);
    --issueheading-text: #831616;

    --example-border: #e0cb52;
    --example-bg: #fcfaee;
    --example-text: var(--text);
    --exampleheading-text: #574b0f;

    --note-border: #52e052;
    --note-bg: #e9fbe9;
    --note-text: var(--text);
    --noteheading-text: hsl(120, 70%, 30%);
    --notesummary-underline: silver;

    --assertion-border: #aaa;
    --assertion-bg: #eee;
    --assertion-text: black;

    --advisement-border: orange;
    --advisement-bg: #fec;
    --advisement-text: var(--text);
    --advisementheading-text: #b35f00;

    --warning-border: red;
    --warning-bg: hsla(40,100%,50%,0.95);
    --warning-text: var(--text);

    --amendment-border: #330099;
    --amendment-bg: #F5F0FF;
    --amendment-text: var(--text);
    --amendmentheading-text: #220066;

    --def-border: #8ccbf2;
    --def-bg: #def;
    --def-text: var(--text);
    --defrow-border: #bbd7e9;

    --datacell-border: silver;

    --indexinfo-text: #707070;

    --indextable-hover-text: black;
    --indextable-hover-bg: #f7f8f9;

    --outdatedspec-bg: rgba(0, 0, 0, .5);
    --outdatedspec-text: black;
    --outdated-bg: maroon;
    --outdated-text: white;
    --outdated-shadow: red;

    --editedrec-bg: darkorange;
}

@media (prefers-color-scheme: dark) {
    :root {
        --text: #ddd;
        --bg: black;

        --unofficial-watermark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cg fill='%23100808' transform='translate(200 200) rotate(-45) translate(-200 -200)' stroke='%23100808' stroke-width='3'%3E%3Ctext x='50%25' y='220' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EUNOFFICIAL%3C/text%3E%3Ctext x='50%25' y='305' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EDRAFT%3C/text%3E%3C/g%3E%3C/svg%3E");

        --logo-bg: #1a5e9a;
        --logo-active-bg: #c00;
        --logo-text: white;

        --tocnav-normal-text: #999;
        --tocnav-normal-bg: var(--bg);
        --tocnav-hover-text: var(--tocnav-normal-text);
        --tocnav-hover-bg: #080808;
        --tocnav-active-text: #f44;
        --tocnav-active-bg: var(--tocnav-normal-bg);

        --tocsidebar-text: var(--text);
        --tocsidebar-bg: #080808;
        --tocsidebar-shadow: rgba(255,255,255,.1);
        --tocsidebar-heading-text: hsla(203,20%,40%,.7);

        --toclink-text: var(--text);
        --toclink-underline: #6af;
        --toclink-visited-text: var(--toclink-text);
        --toclink-visited-underline: #054572;

        --heading-text: #8af;

        --hr-text: var(--text);

        --algo-border: #456;

        --del-text: #f44;
        --del-bg: transparent;
        --ins-text: #4a4;
        --ins-bg: transparent;

        --a-normal-text: #6af;
        --a-normal-underline: #555;
        --a-visited-text: var(--a-normal-text);
        --a-visited-underline: var(--a-normal-underline);
        --a-hover-bg: rgba(25%, 25%, 25%, .2);
        --a-active-text: #f44;
        --a-active-underline: var(--a-active-text);

        --borderedblock-bg: rgba(255, 255, 255, .05);

        --blockquote-border: silver;
        --blockquote-bg: var(--borderedblock-bg);
        --blockquote-text: currentcolor;

        --issue-border: #e05252;
        --issue-bg: var(--borderedblock-bg);
        --issue-text: var(--text);
        --issueheading-text: hsl(0deg, 70%, 70%);

        --example-border: hsl(50deg, 90%, 60%);
        --example-bg: var(--borderedblock-bg);
        --example-text: var(--text);
        --exampleheading-text: hsl(50deg, 70%, 70%);

        --note-border: hsl(120deg, 100%, 35%);
        --note-bg: var(--borderedblock-bg);
        --note-text: var(--text);
        --noteheading-text: hsl(120, 70%, 70%);
        --notesummary-underline: silver;

        --assertion-border: #444;
        --assertion-bg: var(--borderedblock-bg);
        --assertion-text: var(--text);

        --advisement-border: orange;
        --advisement-bg: #222218;
        --advisement-text: var(--text);
        --advisementheading-text: #f84;

        --warning-border: red;
        --warning-bg: hsla(40,100%,20%,0.95);
        --warning-text: var(--text);

        --amendment-border: #330099;
        --amendment-bg: #080010;
        --amendment-text: var(--text);
        --amendmentheading-text: #cc00ff;

        --def-border: #8ccbf2;
        --def-bg: #080818;
        --def-text: var(--text);
        --defrow-border: #136;

        --datacell-border: silver;

        --indexinfo-text: #aaa;

        --indextable-hover-text: var(--text);
        --indextable-hover-bg: #181818;

        --outdatedspec-bg: rgba(255, 255, 255, .5);
        --outdatedspec-text: black;
        --outdated-bg: maroon;
        --outdated-text: white;
        --outdated-shadow: red;

        --editedrec-bg: darkorange;
    }
    /* In case a transparent-bg image doesn't expect to be on a dark bg,
       which is quite common in practice... */
    img { background: white; }
}
</style>
<style>/* Boilerplate: style-counters */
body {
    counter-reset: example figure issue;
}
.issue {
    counter-increment: issue;
}
.issue:not(.no-marker)::before {
    content: "Issue " counter(issue);
}

.example {
    counter-increment: example;
}
.example:not(.no-marker)::before {
    content: "Example " counter(example);
}
.invalid.example:not(.no-marker)::before,
.illegal.example:not(.no-marker)::before {
    content: "Invalid Example" counter(example);
}

figcaption {
    counter-increment: figure;
}
figcaption:not(.no-marker)::before {
    content: "Figure " counter(figure) " ";
}
</style>
<style>/* Boilerplate: style-issues */
a[href].issue-return {
    float: right;
    float: inline-end;
    color: var(--issueheading-text);
    font-weight: bold;
    text-decoration: none;
}
</style>
<style>/* Boilerplate: style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
   regarding paragraph and lists. */
[data-md] > :first-child {
    margin-top: 0;
}
[data-md] > :last-child {
    margin-bottom: 0;
}
</style>
<style>/* Boilerplate: style-selflinks */
:root {
    --selflink-text: white;
    --selflink-bg: gray;
    --selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
    position: relative;
}
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;
}
a.self-link:hover {
    opacity: 1;
}
.heading > a.self-link {
    font-size: 83%;
}
.example > a.self-link,
.note > a.self-link,
.issue > a.self-link {
    /* These blocks are overflow:auto, so positioning outside
       doesn't work. */
    left: auto;
    right: 0;
}
li > a.self-link {
    left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
    top: auto;
    left: auto;
    opacity: 0;
    width: 1.5em;
    height: 1.5em;
    background: var(--selflink-bg);
    color: var(--selflink-text);
    font-style: normal;
    transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
    opacity: 1;
}
dfn > a.self-link:hover {
    color: var(--selflink-hover-text);
}

a.self-link::before            { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before      { content: "#"; }
</style>
<style>/* Boilerplate: style-syntax-highlighting */
code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }

.highlight:not(.idl) { background: rgba(0, 0, 0, .03); }
c-[a] { color: #990055 } /* Keyword.Declaration */
c-[b] { color: #990055 } /* Keyword.Type */
c-[c] { color: #708090 } /* Comment */
c-[d] { color: #708090 } /* Comment.Multiline */
c-[e] { color: #0077aa } /* Name.Attribute */
c-[f] { color: #669900 } /* Name.Tag */
c-[g] { color: #222222 } /* Name.Variable */
c-[k] { color: #990055 } /* Keyword */
c-[l] { color: #000000 } /* Literal */
c-[m] { color: #000000 } /* Literal.Number */
c-[n] { color: #0077aa } /* Name */
c-[o] { color: #999999 } /* Operator */
c-[p] { color: #999999 } /* Punctuation */
c-[s] { color: #a67f59 } /* Literal.String */
c-[t] { color: #a67f59 } /* Literal.String.Single */
c-[u] { color: #a67f59 } /* Literal.String.Double */
c-[cp] { color: #708090 } /* Comment.Preproc */
c-[c1] { color: #708090 } /* Comment.Single */
c-[cs] { color: #708090 } /* Comment.Special */
c-[kc] { color: #990055 } /* Keyword.Constant */
c-[kn] { color: #990055 } /* Keyword.Namespace */
c-[kp] { color: #990055 } /* Keyword.Pseudo */
c-[kr] { color: #990055 } /* Keyword.Reserved */
c-[ld] { color: #000000 } /* Literal.Date */
c-[nc] { color: #0077aa } /* Name.Class */
c-[no] { color: #0077aa } /* Name.Constant */
c-[nd] { color: #0077aa } /* Name.Decorator */
c-[ni] { color: #0077aa } /* Name.Entity */
c-[ne] { color: #0077aa } /* Name.Exception */
c-[nf] { color: #0077aa } /* Name.Function */
c-[nl] { color: #0077aa } /* Name.Label */
c-[nn] { color: #0077aa } /* Name.Namespace */
c-[py] { color: #0077aa } /* Name.Property */
c-[ow] { color: #999999 } /* Operator.Word */
c-[mb] { color: #000000 } /* Literal.Number.Bin */
c-[mf] { color: #000000 } /* Literal.Number.Float */
c-[mh] { color: #000000 } /* Literal.Number.Hex */
c-[mi] { color: #000000 } /* Literal.Number.Integer */
c-[mo] { color: #000000 } /* Literal.Number.Oct */
c-[sb] { color: #a67f59 } /* Literal.String.Backtick */
c-[sc] { color: #a67f59 } /* Literal.String.Char */
c-[sd] { color: #a67f59 } /* Literal.String.Doc */
c-[se] { color: #a67f59 } /* Literal.String.Escape */
c-[sh] { color: #a67f59 } /* Literal.String.Heredoc */
c-[si] { color: #a67f59 } /* Literal.String.Interpol */
c-[sx] { color: #a67f59 } /* Literal.String.Other */
c-[sr] { color: #a67f59 } /* Literal.String.Regex */
c-[ss] { color: #a67f59 } /* Literal.String.Symbol */
c-[vc] { color: #0077aa } /* Name.Variable.Class */
c-[vg] { color: #0077aa } /* Name.Variable.Global */
c-[vi] { color: #0077aa } /* Name.Variable.Instance */
c-[il] { color: #000000 } /* Literal.Number.Integer.Long */

@media (prefers-color-scheme: dark) {
    .highlight:not(.idl) { background: rgba(255, 255, 255, .05); }

    c-[a] { color: #d33682 } /* Keyword.Declaration */
    c-[b] { color: #d33682 } /* Keyword.Type */
    c-[c] { color: #2aa198 } /* Comment */
    c-[d] { color: #2aa198 } /* Comment.Multiline */
    c-[e] { color: #268bd2 } /* Name.Attribute */
    c-[f] { color: #b58900 } /* Name.Tag */
    c-[g] { color: #cb4b16 } /* Name.Variable */
    c-[k] { color: #d33682 } /* Keyword */
    c-[l] { color: #657b83 } /* Literal */
    c-[m] { color: #657b83 } /* Literal.Number */
    c-[n] { color: #268bd2 } /* Name */
    c-[o] { color: #657b83 } /* Operator */
    c-[p] { color: #657b83 } /* Punctuation */
    c-[s] { color: #6c71c4 } /* Literal.String */
    c-[t] { color: #6c71c4 } /* Literal.String.Single */
    c-[u] { color: #6c71c4 } /* Literal.String.Double */
    c-[ch] { color: #2aa198 } /* Comment.Hashbang */
    c-[cp] { color: #2aa198 } /* Comment.Preproc */
    c-[cpf] { color: #2aa198 } /* Comment.PreprocFile */
    c-[c1] { color: #2aa198 } /* Comment.Single */
    c-[cs] { color: #2aa198 } /* Comment.Special */
    c-[kc] { color: #d33682 } /* Keyword.Constant */
    c-[kn] { color: #d33682 } /* Keyword.Namespace */
    c-[kp] { color: #d33682 } /* Keyword.Pseudo */
    c-[kr] { color: #d33682 } /* Keyword.Reserved */
    c-[ld] { color: #657b83 } /* Literal.Date */
    c-[nc] { color: #268bd2 } /* Name.Class */
    c-[no] { color: #268bd2 } /* Name.Constant */
    c-[nd] { color: #268bd2 } /* Name.Decorator */
    c-[ni] { color: #268bd2 } /* Name.Entity */
    c-[ne] { color: #268bd2 } /* Name.Exception */
    c-[nf] { color: #268bd2 } /* Name.Function */
    c-[nl] { color: #268bd2 } /* Name.Label */
    c-[nn] { color: #268bd2 } /* Name.Namespace */
    c-[py] { color: #268bd2 } /* Name.Property */
    c-[ow] { color: #657b83 } /* Operator.Word */
    c-[mb] { color: #657b83 } /* Literal.Number.Bin */
    c-[mf] { color: #657b83 } /* Literal.Number.Float */
    c-[mh] { color: #657b83 } /* Literal.Number.Hex */
    c-[mi] { color: #657b83 } /* Literal.Number.Integer */
    c-[mo] { color: #657b83 } /* Literal.Number.Oct */
    c-[sa] { color: #6c71c4 } /* Literal.String.Affix */
    c-[sb] { color: #6c71c4 } /* Literal.String.Backtick */
    c-[sc] { color: #6c71c4 } /* Literal.String.Char */
    c-[dl] { color: #6c71c4 } /* Literal.String.Delimiter */
    c-[sd] { color: #6c71c4 } /* Literal.String.Doc */
    c-[se] { color: #6c71c4 } /* Literal.String.Escape */
    c-[sh] { color: #6c71c4 } /* Literal.String.Heredoc */
    c-[si] { color: #6c71c4 } /* Literal.String.Interpol */
    c-[sx] { color: #6c71c4 } /* Literal.String.Other */
    c-[sr] { color: #6c71c4 } /* Literal.String.Regex */
    c-[ss] { color: #6c71c4 } /* Literal.String.Symbol */
    c-[fm] { color: #268bd2 } /* Name.Function.Magic */
    c-[vc] { color: #cb4b16 } /* Name.Variable.Class */
    c-[vg] { color: #cb4b16 } /* Name.Variable.Global */
    c-[vi] { color: #cb4b16 } /* Name.Variable.Instance */
    c-[vm] { color: #cb4b16 } /* Name.Variable.Magic */
    c-[il] { color: #657b83 } /* Literal.Number.Integer.Long */
}
</style>
 <body class="h-entry">
  <div class="head">
   <p data-fill-with="logo"></p>
   <h1 class="p-name no-ref" id="title">P3107R5<br>Permit an efficient implementation of std::print</h1>
   <h2 class="no-num no-toc no-ref heading settled" id="profile-and-date"><span class="content">Published Proposal, <time class="dt-updated" datetime="2024-03-21">2024-03-21</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt class="editor">Author:
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:victor.zverovich@gmail.com">Victor Zverovich</a>
     <dt>Audience:
     <dd>LWG
     <dt>Project:
     <dd>ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
    </dl>
   </div>
   <div data-fill-with="warning"></div>
   <hr title="Separator for header">
  </div>
  <nav data-fill-with="table-of-contents" id="toc">
   <h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
   <ol class="toc" role="directory">
    <li><a href="#intro"><span class="secno">1</span> <span class="content">Introduction</span></a>
    <li><a href="#changes4"><span class="secno">2</span> <span class="content">Changes since R4</span></a>
    <li><a href="#changes3"><span class="secno">3</span> <span class="content">Changes since R3</span></a>
    <li><a href="#changes2"><span class="secno">4</span> <span class="content">Changes since R2</span></a>
    <li><a href="#changes1"><span class="secno">5</span> <span class="content">Changes since R1</span></a>
    <li><a href="#changes0"><span class="secno">6</span> <span class="content">Changes since R0</span></a>
    <li><a href="#polls"><span class="secno">7</span> <span class="content">Polls</span></a>
    <li><a href="#problem"><span class="secno">8</span> <span class="content">Problems</span></a>
    <li><a href="#proposal"><span class="secno">9</span> <span class="content">Proposal</span></a>
    <li><a href="#perf"><span class="secno">10</span> <span class="content">Performance</span></a>
    <li><a href="#impl"><span class="secno">11</span> <span class="content">Implementation</span></a>
    <li><a href="#wording"><span class="secno">12</span> <span class="content">Wording</span></a>
    <li><a href="#ack"><span class="secno">13</span> <span class="content">Acknowledgements</span></a>
    <li>
     <a href="#references"><span class="secno"></span> <span class="content">References</span></a>
     <ol class="toc">
      <li><a href="#informative"><span class="secno"></span> <span class="content">Informative References</span></a>
     </ol>
   </ol>
  </nav>
  <main>
   <h2 class="heading settled" data-level="1" id="intro"><span class="secno">1. </span><span class="content">Introduction</span><a class="self-link" href="#intro"></a></h2>
   <p>C++23 introduced a new formatted output facility, <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>print</c-></code> (<a data-link-type="biblio" href="#biblio-p2093" title="Formatted output">[P2093]</a>).
It was defined in terms of formatting into a temporary <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code> to simplify
the specification and to clearly indicate the requirement for non-interleaved
output. Unfortunately, it was discovered that this approach does not allow for a
more efficient implementation strategy, such as writing directly to a stream
buffer under a lock, as reported in <a data-link-type="biblio" href="#biblio-lwg4042" title="LWG Issue 4042: `std::print` should permit an efficient implementation">[LWG4042]</a>. This paper proposes a solution
to address this shortcoming.</p>
   <h2 class="heading settled" data-level="2" id="changes4"><span class="secno">2. </span><span class="content">Changes since R4</span><a class="self-link" href="#changes4"></a></h2>
   <ul>
    <li data-md>
     <p>Replaced <code class="highlight"><c- n>Arg</c-></code> with <code class="highlight"><c- n>Args</c-></code> in the <em>Effects</em> clause of <code class="highlight"><c- n>print</c-></code>.</p>
    <li data-md>
     <p>Added missing parentheses around the fold expression in the <em>Effects</em> clause
of <code class="highlight"><c- n>print</c-></code>.</p>
    <li data-md>
     <p>Added a missing call to <code class="highlight"><c- n>make_format_args</c-></code> in the call to <code class="highlight"><c- n>vprint_unicode_locking</c-></code> in the <em>Effects</em> clause of <code class="highlight"><c- n>vprint_unicode</c-></code>.</p>
    <li data-md>
     <p>Added a missing call to <code class="highlight"><c- n>make_format_args</c-></code> in the call to <code class="highlight"><c- n>vprint_nonunicode_locking</c-></code> in the <em>Effects</em> clause of <code class="highlight"><c- n>vprint_nonunicode</c-></code>.</p>
    <li data-md>
     <p>Replaced "Releases the lock" with "Unconditionally unlocks <code class="highlight"><c- n>stream</c-></code> on
function exit" in the <em>Effects</em> clause of <code class="highlight"><c- n>vprint_unicode_locking</c-></code>.</p>
    <li data-md>
     <p>Made it clear in the formatting that <code class="highlight"><c- n>vprint_unicode</c-></code> and <code class="highlight"><c- n>vprint_nonunicode</c-></code> are now defined in terms of their locking counterparts.</p>
    <li data-md>
     <p>Changed "Locks <code class="highlight"><c- n>stream</c-></code>, ... and releases the lock" to "While holding the lock
on <code class="highlight"><c- n>stream</c-></code>, ...".</p>
    <li data-md>
     <p>Changed the wording of how the standard formatters opt in per suggestion from
Tomasz Kamiński.</p>
   </ul>
   <h2 class="heading settled" data-level="3" id="changes3"><span class="secno">3. </span><span class="content">Changes since R3</span><a class="self-link" href="#changes3"></a></h2>
   <ul>
    <li data-md>
     <p>Changed <code class="highlight"><c- n>has_locking_formatter</c-></code> to <code class="highlight"><c- n>enable_nonlocking_formatter_optimization</c-></code> and reverse the meaning per LEWG request.</p>
    <li data-md>
     <p>Added LEWG poll results for R2 and R3.</p>
   </ul>
   <h2 class="heading settled" data-level="4" id="changes2"><span class="secno">4. </span><span class="content">Changes since R2</span><a class="self-link" href="#changes2"></a></h2>
   <ul>
    <li data-md>
     <p>Changed the opt-in mechanism to use a namespace-scoped variable template
instead of a nested member to prevent the inheritance issue.</p>
   </ul>
   <h2 class="heading settled" data-level="5" id="changes1"><span class="secno">5. </span><span class="content">Changes since R1</span><a class="self-link" href="#changes1"></a></h2>
   <ul>
    <li data-md>
     <p>Made the new behavior an opt in for user-defined formatters to prevent
potential deadlocks when they perform locking in their <code class="highlight"><c- n>format</c-></code> functions.</p>
    <li data-md>
     <p>Added a missing <code class="highlight"><c- n>stream</c-></code> argument in the call to <code class="highlight"><c- n>print</c-></code> in the <em>Effects</em> clause of <code class="highlight"><c- n>println</c-></code>.</p>
    <li data-md>
     <p>Added instructions to update the <code class="highlight"><c- n>__cpp_lib_print</c-></code> feature testing macro.</p>
    <li data-md>
     <p>Provided an example illustrating a problem with interleaved output.</p>
    <li data-md>
     <p>Provided an example illustrating a problem with locking in C++ and Java.</p>
   </ul>
   <h2 class="heading settled" data-level="6" id="changes0"><span class="secno">6. </span><span class="content">Changes since R0</span><a class="self-link" href="#changes0"></a></h2>
   <ul>
    <li data-md>
     <p>Added preliminary results for libstdc++ provided by Jonathan Wakely.</p>
    <li data-md>
     <p>Replaced the definition of <code class="highlight"><c- n>println</c-></code> with a more efficient one that doesn’t
call <code class="highlight"><c- n>format</c-></code>.</p>
    <li data-md>
     <p>Fixed typos.</p>
   </ul>
   <h2 class="heading settled" data-level="7" id="polls"><span class="secno">7. </span><span class="content">Polls</span><a class="self-link" href="#polls"></a></h2>
   <p>LEWG poll results for R3:</p>
   <p><strong>POLL</strong>: Change <code class="highlight"><c- n>has_locking_formatter</c-></code> to <code class="highlight"><c- n>enable_nonlocking_formatter_optimization</c-></code> and reverse the boolean polarity</p>
<pre class="language-text highlight">SF  F  N  A SA
 4 10  3  0  0
</pre>
   <p><strong>Outcome</strong>: Consensus</p>
   <p><strong>POLL</strong>: Modify P3107R3 (Permit an efficient implementation of std::print) by
performing the modifications from the prior poll, and then send the revised
paper to LWG for C++26 with a recommendation that implementations backport the
change, classified as B2 (bug fix), to be confirmed with a Library Evolution
electronic poll.</p>
<pre class="language-text highlight">SF  F  N  A SA
10  7  1  1  1
</pre>
   <p><strong>Outcome</strong>: Consensus</p>
   <p>LEWG poll results for R2:</p>
   <p><strong>POLL</strong>: We want to see in a follow-up paper of an investigation of removing
the recommendation (from the standard) or forbidding inheritance in order to
customize formatters.</p>
<pre class="language-text highlight">SF  F  N  A SA
 4 11  9  7  0
</pre>
   <p><strong>Outcome</strong>: Consensus in favor</p>
   <p><strong>POLL</strong>: The default should assume the user formatter might be locking, types
that don’t lock should explicitly state so to achieve performance.</p>
<pre class="language-text highlight">SF  F  N  A SA
10 18  5  0  0
</pre>
   <p><strong>Outcome</strong>: Strong Consensus in Favor</p>
   <h2 class="heading settled" data-level="8" id="problem"><span class="secno">8. </span><span class="content">Problems</span><a class="self-link" href="#problem"></a></h2>
   <p>As reported in <a data-link-type="biblio" href="#biblio-lwg4042" title="LWG Issue 4042: `std::print` should permit an efficient implementation">[LWG4042]</a>, <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>print</c-></code>/<code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>vprint</c-><c- o>*</c-></code> is currently defined in
terms of formatting into a temporary <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code>, e.g. <a href="https://eel.is/c++draft/print.fun">[print.fun</a>]:</p>
   <blockquote>
    <p><code class="highlight"><c- b>void</c-> <c- nf>vprint_nonunicode</c-><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>format_args</c-> <c- n>args</c-><c- p>);</c-></code></p>
    <p><em>Preconditions</em>: <code class="highlight"><c- n>stream</c-></code> is a valid pointer to an output C stream.</p>
    <p><em>Effects</em>: Writes the result of <code class="highlight"><c- n>vformat</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>)</c-></code> to <code class="highlight"><c- n>stream</c-></code>.</p>
    <p><em>Throws</em>: Any exception thrown by the call to <code class="highlight"><c- n>vformat</c-></code> (<a href="https://eel.is/c++draft/format.err.report">[format.err.report</a>]). <code class="highlight"><c- n>system_error</c-></code> if writing to <code class="highlight"><c- n>stream</c-></code> fails. May throw <code class="highlight"><c- n>bad_alloc</c-></code>.</p>
   </blockquote>
   <p>This prohibits a more efficient implementation strategy of formatting directly
into a stream buffer under a lock (<code class="highlight"><c- n>flockfile</c-></code>/<code class="highlight"><c- n>funlockfile</c-></code> in POSIX, <a data-link-type="biblio" href="#biblio-stdio-lock" title="The Open Group Base Specifications Issue 7, 2018 edition. IEEE Std 1003.1-2017. flockfile, ftrylockfile, funlockfile - stdio locking functions">[STDIO-LOCK]</a>) like C stdio and other formatting facilities do.</p>
   <p>The inability to achieve this with the current wording stems from the observable
effects: throwing an exception from a user-defined formatter currently prevents
any output from a formatting function, whereas with the direct method, the
output written to the stream before the exception occurred is preserved. Most
errors are caught at compile time, making this situation uncommon. The current
behavior can be easily replicated by explicitly formatting into an intermediate
string or buffer.</p>
   <p>Another problem is that such double buffering may require unbounded memory
allocations, making <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>print</c-></code> unsuitable for resource-constrained
applications creating incentives for continued use of unsafe APIs. In the direct
method, there are usually no memory allocations.</p>
   <h2 class="heading settled" data-level="9" id="proposal"><span class="secno">9. </span><span class="content">Proposal</span><a class="self-link" href="#proposal"></a></h2>
   <p>The current paper proposes expressing the desire to have non-interleaved
output in a way that permits a more efficient implementation similar
to <code class="highlight"><c- n>printf</c-></code>’s. It is based on the locking mechanism provided by C streams,
quoting Section 7.21.2 Streams of the C standard (<a data-link-type="biblio" href="#biblio-n2310-streams" title="7.21.2 Streams. ISO/IEC 9899:202x. Programming languages — C">[N2310-STREAMS]</a>):</p>
   <blockquote>
    <p>7 Each stream has an associated lock that is used to prevent data races
when multiple threads of execution access a stream, and to restrict the
interleaving of stream operations performed by multiple threads. Only one
thread may hold this lock at a time. The lock is reentrant: a single thread
may hold the lock multiple times at a given time.</p>
    <p>8 All functions that read, write, position, or query the position of a stream
lock the stream before accessing it. They release the lock associated with the
stream when the access is complete.</p>
   </blockquote>
   <p>As shown in <a href="#perf">Performance</a>, this can give more than 20% speed up even
compared to writing to a stack-allocated buffer.</p>
   <p>All of the following languages use an implementation consistent with the current
proposal (no intermediate buffering):</p>
   <ul>
    <li data-md>
     <p>C (<code class="highlight"><c- n>printf</c-></code>)</p>
    <li data-md>
     <p>Rust (<code class="highlight"><c- n>println</c-><c- o>!</c-></code>)</p>
    <li data-md>
     <p>Java (<code class="highlight"><c- n>System</c-><c- p>.</c-><c- n>out</c-><c- p>.</c-><c- n>format</c-></code>)</p>
   </ul>
   <p>IOStreams don’t provide atomicity which is even weaker than the guarantees
provided by these languages and the current proposal. For example:</p>
<pre class="language-c++ highlight"><c- cp>#include</c-> &lt;iostream>
<c- cp>#include</c-> &lt;thread>

<c- b>void</c-> <c- nf>worker</c-><c- p>()</c-> <c- p>{</c->
  <c- k>for</c-> <c- p>(</c-><c- b>int</c-> <c- n>i</c-> <c- o>=</c-> <c- mi>0</c-><c- p>;</c-> <c- n>i</c-> <c- o>&lt;</c-> <c- mi>3</c-><c- p>;</c-> <c- o>++</c-><c- n>i</c-><c- p>)</c-> <c- p>{</c->
    <c- c1>// Simulate work.</c->
    <c- n>std</c-><c- o>::</c-><c- n>this_thread</c-><c- o>::</c-><c- n>sleep_for</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>chrono</c-><c- o>::</c-><c- n>milliseconds</c-><c- p>(</c-><c- mi>200</c-><c- p>));</c->
    <c- n>std</c-><c- o>::</c-><c- n>cout</c-> <c- o>&lt;&lt;</c-> <c- s>"thread "</c-> <c- o>&lt;&lt;</c-> <c- n>std</c-><c- o>::</c-><c- n>this_thread</c-><c- o>::</c-><c- n>get_id</c-><c- p>()</c->
      <c- o>&lt;&lt;</c-> <c- s>": work"</c-> <c- o>&lt;&lt;</c-> <c- n>i</c-> <c- o>&lt;&lt;</c-> <c- s>" done</c-><c- se>\n</c-><c- s>"</c-><c- p>;</c->
  <c- p>}</c->
<c- p>}</c->

<c- b>int</c-> <c- nf>main</c-><c- p>()</c-> <c- p>{</c->
  <c- k>auto</c-> <c- n>t1</c-> <c- o>=</c-> <c- n>std</c-><c- o>::</c-><c- n>jthread</c-><c- p>(</c-><c- n>worker</c-><c- p>);</c->
  <c- k>auto</c-> <c- n>t2</c-> <c- o>=</c-> <c- n>std</c-><c- o>::</c-><c- n>jthread</c-><c- p>(</c-><c- n>worker</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>may produce the following output:</p>
<pre class="highlight"><c- kr>thread</c-> <c- mi>140239754491456</c-><c- o>:</c-> <c- n>work0</c-> <c- n>done</c->
<c- kr>thread</c-> <c- mi>140239746098752</c-><c- o>:</c-> <c- n>work0</c-> <c- n>done</c->
<c- kr>thread</c-> <c- kr>thread</c-> <c- mi>140239746098752</c-><c- o>:</c-> <c- n>work140239754491456</c-><c- o>:</c-> <c- n>work1</c-> <c- n>done</c->
<c- mi>1</c-> <c- n>done</c->
<c- kr>thread</c-> <c- mi>140239754491456</c-><c- o>:</c-> <c- n>work2</c-> <c- n>done</c->
<c- kr>thread</c-> <c- mi>140239746098752</c-><c- o>:</c-> <c- n>work2</c-> <c- n>done</c->
</pre>
   <p>Neither <code class="highlight"><c- n>printf</c-></code> nor <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>print</c-></code> have this issue.</p>
   <p>One problem with locking a stream is that it may introduce potential for
deadlocks in case a user-defined formatter is also doing locking internally.
For example:</p>
<pre class="language-c++ highlight"><c- k>struct</c-> <c- nc>deadlockable</c-> <c- p>{</c->
  <c- b>int</c-> <c- n>value</c-> <c- o>=</c-> <c- mi>0</c-><c- p>;</c->
  <c- n>mutable</c-> <c- n>std</c-><c- o>::</c-><c- n>mutex</c-> <c- n>mutex</c-><c- p>;</c->
<c- p>};</c->

<c- n>template</c-> <c- o>&lt;></c-> <c- k>struct</c-> <c- nc>std</c-><c- o>::</c-><c- n>formatter</c-><c- o>&lt;</c-><c- n>deadlockable</c-><c- o>></c-> <c- p>{</c->
  <c- n>constexpr</c-> <c- k>auto</c-> <c- n>parse</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>format_parse_context</c-><c- o>&amp;</c-> <c- n>ctx</c-><c- p>)</c-> <c- p>{</c->
    <c- k>return</c-> <c- n>ctx</c-><c- p>.</c-><c- n>begin</c-><c- p>();</c->
  <c- p>}</c->

  <c- k>auto</c-> <c- n>format</c-><c- p>(</c-><c- k>const</c-> <c- n>deadlockable</c-><c- o>&amp;</c-> <c- n>d</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>format_context</c-><c- o>&amp;</c-> <c- n>ctx</c-><c- p>)</c-> <c- k>const</c-> <c- p>{</c->
    <c- n>std</c-><c- o>::</c-><c- n>lock_guard</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>mutex</c-><c- o>></c-> <c- n>lock</c-><c- p>(</c-><c- n>d</c-><c- p>.</c-><c- n>mutex</c-><c- p>);</c->
    <c- k>return</c-> <c- n>std</c-><c- o>::</c-><c- n>format_to</c-><c- p>(</c-><c- n>ctx</c-><c- p>.</c-><c- n>out</c-><c- p>(),</c-> <c- s>"{}"</c-><c- p>,</c-> <c- n>d</c-><c- p>.</c-><c- n>value</c-><c- p>);</c->
  <c- p>}</c->
<c- p>};</c->

<c- n>deadlockable</c-> <c- n>d</c-><c- p>;</c->
<c- k>auto</c-> <c- n>t</c-> <c- o>=</c-> <c- n>std</c-><c- o>::</c-><c- kr>thread</c-><c- p>([</c-><c- o>&amp;</c-><c- p>]()</c-> <c- p>{</c->
  <c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"start</c-><c- se>\n</c-><c- s>"</c-><c- p>);</c->
  <c- n>std</c-><c- o>::</c-><c- n>lock_guard</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>mutex</c-><c- o>></c-> <c- n>lock</c-><c- p>(</c-><c- n>d</c-><c- p>.</c-><c- n>mutex</c-><c- p>);</c->
  <c- k>for</c-> <c- p>(</c-><c- b>int</c-> <c- n>i</c-> <c- o>=</c-> <c- mi>0</c-><c- p>;</c-> <c- n>i</c-> <c- o>&lt;</c-> <c- mi>1000000</c-><c- p>;</c-> <c- o>++</c-><c- n>i</c-><c- p>)</c-> <c- n>d</c-><c- p>.</c-><c- n>value</c-> <c- o>+=</c-> <c- mi>10</c-><c- p>;</c->
  <c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"done</c-><c- se>\n</c-><c- s>"</c-><c- p>);</c->
<c- p>});</c->
<c- k>for</c-> <c- p>(</c-><c- b>int</c-> <c- n>i</c-> <c- o>=</c-> <c- mi>0</c-><c- p>;</c-> <c- n>i</c-> <c- o>&lt;</c-> <c- mi>100</c-><c- p>;</c-> <c- o>++</c-><c- n>i</c-><c- p>)</c-> <c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"{}"</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>t</c-><c- p>.</c-><c- n>join</c-><c- p>();</c->
</pre>
   <p>The same problem exists in other languages, for example:</p>
<pre class="language-java highlight"><c- a>class</c-> <c- nc>Deadlockable</c-> <c- p>{</c->
  <c- a>public</c-> <c- b>int</c-> <c- n>value</c-><c- p>;</c->
  <c- a>public</c-> <c- n>String</c-> <c- nf>toString</c-><c- p>()</c-> <c- p>{</c->
    <c- a>synchronized</c-> <c- p>(</c-><c- k>this</c-><c- p>)</c-> <c- p>{</c->
      <c- k>return</c-> <c- n>Integer</c-><c- p>.</c-><c- e>toString</c-><c- p>(</c-><c- n>value</c-><c- p>);</c->
    <c- p>}</c->
  <c- p>}</c->
<c- p>}</c->

<c- a>class</c-> <c- nc>Hello</c-> <c- p>{</c->
  <c- a>public</c-> <c- a>static</c-> <c- b>void</c-> <c- nf>main</c-><c- p>(</c-><c- n>String</c-><c- o>[]</c-> <c- n>args</c-><c- p>)</c-> <c- a>throws</c-> <c- n>InterruptedException</c-> <c- p>{</c->
    <c- n>Deadlockable</c-> <c- n>d</c-> <c- o>=</c-> <c- k>new</c-> <c- n>Deadlockable</c-><c- p>();</c->

    <c- n>Thread</c-> <c- n>t</c-> <c- o>=</c-> <c- k>new</c-> <c- n>Thread</c-><c- p>(</c-><c- k>new</c-> <c- n>Runnable</c-><c- p>()</c-> <c- p>{</c->
      <c- a>private</c-> <c- n>Deadlockable</c-> <c- n>d</c-><c- p>;</c->

      <c- a>public</c-> <c- n>Runnable</c-> <c- nf>init</c-><c- p>(</c-><c- n>Deadlockable</c-> <c- n>d</c-><c- p>)</c-> <c- p>{</c->
        <c- k>this</c-><c- p>.</c-><c- e>d</c-> <c- o>=</c-> <c- n>d</c-><c- p>;</c->
        <c- k>return</c-> <c- k>this</c-><c- p>;</c->
      <c- p>}</c->

      <c- nd>@Override</c->
      <c- a>public</c-> <c- b>void</c-> <c- nf>run</c-><c- p>()</c-> <c- p>{</c->
        <c- n>System</c-><c- p>.</c-><c- e>out</c-><c- p>.</c-><c- e>println</c-><c- p>(</c-><c- s>"start"</c-><c- p>);</c->
        <c- a>synchronized</c-> <c- p>(</c-><c- n>d</c-><c- p>)</c-> <c- p>{</c->
          <c- k>for</c-> <c- p>(</c-><c- b>int</c-> <c- n>i</c-> <c- o>=</c-> <c- mi>0</c-><c- p>;</c-> <c- n>i</c-> <c- o>&lt;</c-> <c- mi>1000000</c-><c- p>;</c-> <c- o>++</c-><c- n>i</c-><c- p>)</c-> <c- n>d</c-><c- p>.</c-><c- e>value</c-> <c- o>+=</c-> <c- mi>10</c-><c- p>;</c->
          <c- n>System</c-><c- p>.</c-><c- e>out</c-><c- p>.</c-><c- e>format</c-><c- p>(</c-><c- s>"done"</c-><c- p>);</c->
        <c- p>}</c->
      <c- p>}</c->
    <c- p>}.</c-><c- e>init</c-><c- p>(</c-><c- n>d</c-><c- p>));</c->
    <c- n>t</c-><c- p>.</c-><c- e>start</c-><c- p>();</c->
    <c- k>for</c-> <c- p>(</c-><c- b>int</c-> <c- n>i</c-> <c- o>=</c-> <c- mi>0</c-><c- p>;</c-> <c- n>i</c-> <c- o>&lt;</c-> <c- mi>100</c-><c- p>;</c-> <c- o>++</c-><c- n>i</c-><c- p>)</c-> <c- n>System</c-><c- p>.</c-><c- e>out</c-><c- p>.</c-><c- e>format</c-><c- p>(</c-><c- s>"%s"</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
    <c- n>t</c-><c- p>.</c-><c- e>join</c-><c- p>();</c->
  <c- p>}</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- n>Found</c-> <c- n>one</c-> <c- n>Java</c-><c- o>-</c-><c- n>level</c-> <c- n>deadlock</c-><c- o>:</c->
<c- o>=============================</c->
<c- s>"main"</c-><c- o>:</c->
  <c- n>waiting</c-> <c- n>to</c-> <c- n>lock</c-> <c- n>monitor</c-> <c- mh>0x0000600002fb4750</c-> <c- p>(</c-><c- n>object</c-> <c- mh>0x000000070fe120e8</c-><c- p>,</c-> <c- n>a</c-> <c- n>Deadlockable</c-><c- p>),</c->
  <c- n>which</c-> <c- n>is</c-> <c- n>held</c-> <c- n>by</c-> <c- s>"Thread-0"</c->

<c- s>"Thread-0"</c-><c- o>:</c->
  <c- n>waiting</c-> <c- k>for</c-> <c- n>ownable</c-> <c- n>synchronizer</c-> <c- mh>0x000000070fe08998</c-><c- p>,</c-> <c- p>(</c-><c- n>a</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>ReentrantLock$NonfairSync</c-><c- p>),</c->
  <c- n>which</c-> <c- n>is</c-> <c- n>held</c-> <c- n>by</c-> <c- s>"main"</c->

<c- n>Java</c-> <c- n>stack</c-> <c- n>information</c-> <c- k>for</c-> <c- n>the</c-> <c- n>threads</c-> <c- n>listed</c-> <c- n>above</c-><c- o>:</c->
<c- o>===================================================</c->
<c- s>"main"</c-><c- o>:</c->
  <c- n>at</c-> <c- n>Deadlockable</c-><c- p>.</c-><c- n>toString</c-><c- p>(</c-><c- n>Hello</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>5</c-><c- p>)</c->
  <c- o>-</c-> <c- n>waiting</c-> <c- n>to</c-> <c- n>lock</c-> <c- o>&lt;</c-><c- mh>0x000000070fe120e8</c-><c- o>></c-> <c- p>(</c-><c- n>a</c-> <c- n>Deadlockable</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>Formatter$FormatSpecifier</c-><c- p>.</c-><c- n>printString</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>Formatter</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>3158</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>Formatter$FormatSpecifier</c-><c- p>.</c-><c- n>print</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>Formatter</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>3036</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>Formatter</c-><c- p>.</c-><c- n>format</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>Formatter</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>2791</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>io</c-><c- p>.</c-><c- n>PrintStream</c-><c- p>.</c-><c- n>implFormat</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>PrintStream</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>1367</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>io</c-><c- p>.</c-><c- n>PrintStream</c-><c- p>.</c-><c- n>format</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>PrintStream</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>1346</c-><c- p>)</c->
  <c- n>at</c-> <c- n>Hello</c-><c- p>.</c-><c- n>main</c-><c- p>(</c-><c- n>Hello</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>32</c-><c- p>)</c->
<c- s>"Thread-0"</c-><c- o>:</c->
  <c- n>at</c-> <c- n>jdk</c-><c- p>.</c-><c- n>internal</c-><c- p>.</c-><c- n>misc</c-><c- p>.</c-><c- n>Unsafe</c-><c- p>.</c-><c- n>park</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>Native</c-> <c- n>Method</c-><c- p>)</c->
  <c- o>-</c-> <c- n>parking</c-> <c- n>to</c-> <c- n>wait</c-> <c- k>for</c->  <c- o>&lt;</c-><c- mh>0x000000070fe08998</c-><c- o>></c-> <c- p>(</c-><c- n>a</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>ReentrantLock$NonfairSync</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>LockSupport</c-><c- p>.</c-><c- n>park</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>LockSupport</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>221</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>AbstractQueuedSynchronizer</c-><c- p>.</c-><c- n>acquire</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>AbstractQueuedSynchronizer</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>754</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>AbstractQueuedSynchronizer</c-><c- p>.</c-><c- n>acquire</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>AbstractQueuedSynchronizer</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>990</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>ReentrantLock$Sync</c-><c- p>.</c-><c- n>lock</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>ReentrantLock</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>153</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>util</c-><c- p>.</c-><c- n>concurrent</c-><c- p>.</c-><c- n>locks</c-><c- p>.</c-><c- n>ReentrantLock</c-><c- p>.</c-><c- n>lock</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>ReentrantLock</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>322</c-><c- p>)</c->
  <c- n>at</c-> <c- n>jdk</c-><c- p>.</c-><c- n>internal</c-><c- p>.</c-><c- n>misc</c-><c- p>.</c-><c- n>InternalLock</c-><c- p>.</c-><c- n>lock</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>InternalLock</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>74</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>io</c-><c- p>.</c-><c- n>PrintStream</c-><c- p>.</c-><c- n>format</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>PrintStream</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>1344</c-><c- p>)</c->
  <c- n>at</c-> <c- n>Hello$1</c-><c- p>.</c-><c- n>run</c-><c- p>(</c-><c- n>Hello</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>27</c-><c- p>)</c->
  <c- o>-</c-> <c- n>locked</c-> <c- o>&lt;</c-><c- mh>0x000000070fe120e8</c-><c- o>></c-> <c- p>(</c-><c- n>a</c-> <c- n>Deadlockable</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>lang</c-><c- p>.</c-><c- n>Thread</c-><c- p>.</c-><c- n>runWith</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>Thread</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>1596</c-><c- p>)</c->
  <c- n>at</c-> <c- n>java</c-><c- p>.</c-><c- n>lang</c-><c- p>.</c-><c- n>Thread</c-><c- p>.</c-><c- n>run</c-><c- p>(</c-><c- n>java</c-><c- p>.</c-><c- n>base</c->@<c- mf>21.0.2</c-><c- o>/</c-><c- n>Thread</c-><c- p>.</c-><c- n>java</c-><c- o>:</c-><c- mi>1583</c-><c- p>)</c->

<c- n>Found</c-> <c- mi>1</c-> <c- n>deadlock</c-><c- p>.</c->
</pre>
   <p>This is obviously bad code because it unnecessarily calls <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>print</c-></code> / <code class="highlight"><c- n>System</c-><c- p>.</c-><c- n>out</c-><c- p>.</c-><c- n>format</c-></code> under a lock but it is still undesirable to have it
deadlocked.</p>
   <p>To prevent deadlocks while still providing major performance improvements and
preventing dynamic allocations for the common case, this paper proposes making
user-defined formatters opt into the new behavior. Standard formatters are
nonlocking and will be opted in which means that <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>print</c-></code> can be used as
a replacement for all current uses of <code class="highlight"><c- n>printf</c-></code> without concerns that it causes
unbounded memory allocation.</p>
   <p>The opt in is done via the variable template similarly to <code class="highlight"><c- n>enable_borrowed_range</c-></code> and <code class="highlight"><c- n>format_kind</c-></code>:</p>
<pre class="highlight"><c- k>struct</c-> <c- nc>foo</c-> <c- p>{};</c->

<c- k>template</c-> <c- o>&lt;></c-> <c- k>struct</c-> <c- nc>std</c-><c- o>::</c-><c- n>formatter</c-><c- o>&lt;</c-><c- n>foo</c-><c- o>></c-> <c- p>{</c->
  <c- c1>// ...</c->
<c- p>};</c->

<c- k>template</c-> <c- o>&lt;></c->
<c- k>constexpr</c-> <c- b>bool</c-> <c- n>std</c-><c- o>::</c-><c- n>enable_nonlocking_formatter_optimization</c-><c- o>&lt;</c-><c- n>foo</c-><c- o>></c-> <c- o>=</c-> true<c- p>;</c->
</pre>
   <p>R2 of the paper used the <code class="highlight"><c- k>static</c-> <c- k>constexpr</c-> <c- b>bool</c-></code> member variable:</p>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;></c-> <c- k>struct</c-> <c- nc>std</c-><c- o>::</c-><c- n>formatter</c-><c- o>&lt;</c-><c- n>foo</c-><c- o>></c-> <c- p>{</c->
  <c- k>static</c-> <c- k>constexpr</c-> <c- b>bool</c-> <c- n>locking</c-> <c- o>=</c-> false<c- p>;</c->
  <c- c1>// ...</c->
<c- p>};</c->
</pre>
   <p>The advantage of the variable template is that it doesn’t propagate when one <code class="highlight"><c- n>formatter</c-></code> is inherited from another.</p>
   <h2 class="heading settled" data-level="10" id="perf"><span class="secno">10. </span><span class="content">Performance</span><a class="self-link" href="#perf"></a></h2>
   <p>The following benchmark demonstrates the difference in performance between
different implementation strategies using the reference implementation of <code class="highlight"><c- n>print</c-></code> from <a data-link-type="biblio" href="#biblio-fmt" title="The {fmt} library">[FMT]</a>. This benchmark is based on the one from <a data-link-type="biblio" href="#biblio-p2093" title="Formatted output">[P2093]</a> but
modified to avoid the small string optimization effects. It formats a simple
message and prints it to the output stream redirected to <code class="highlight"><c- o>/</c-><c- n>dev</c-><c- o>/</c-><c- n>null</c-></code>. It uses
the Google Benchmark library <a data-link-type="biblio" href="#biblio-google-bench" title="Google Benchmark: A microbenchmark support library">[GOOGLE-BENCH]</a> to measure timings:</p>
<pre class="highlight"><c- cp>#include</c-> &lt;cstdio>
<c- cp>#include</c-> &lt;benchmark/benchmark.h>
<c- cp>#include</c-> &lt;fmt/format.h>

<c- b>void</c-> <c- nf>printf</c-><c- p>(</c-><c- n>benchmark</c-><c- o>::</c-><c- n>State</c-><c- o>&amp;</c-> <c- n>s</c-><c- p>)</c-> <c- p>{</c->
  <c- k>while</c-> <c- p>(</c-><c- n>s</c-><c- p>.</c-><c- n>KeepRunning</c-><c- p>())</c->
    <c- n>std</c-><c- o>::</c-><c- n>printf</c-><c- p>(</c-><c- s>"The answer to life, the universe, and everything is %d.</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- mi>42</c-><c- p>);</c->
<c- p>}</c->
<c- n>BENCHMARK</c-><c- p>(</c-><c- n>printf</c-><c- p>);</c->

<c- b>void</c-> <c- nf>vprint_string</c-><c- p>(</c-><c- n>fmt</c-><c- o>::</c-><c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>fmt</c-><c- o>::</c-><c- n>format_args</c-> <c- n>args</c-><c- p>)</c-> <c- p>{</c->
  <c- k>auto</c-> <c- n>s</c-> <c- o>=</c-> <c- n>fmt</c-><c- o>::</c-><c- n>vformat</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>);</c->
  <c- b>int</c-> <c- n>result</c-> <c- o>=</c-> <c- n>fwrite</c-><c- p>(</c-><c- n>s</c-><c- p>.</c-><c- n>data</c-><c- p>(),</c-> <c- mi>1</c-><c- p>,</c-> <c- n>s</c-><c- p>.</c-><c- n>size</c-><c- p>(),</c-> <c- n>stdout</c-><c- p>);</c->
  <c- k>if</c-> <c- p>(</c-><c- n>result</c-> <c- o>&lt;</c-> <c- n>s</c-><c- p>.</c-><c- n>size</c-><c- p>())</c-> <c- k>throw</c-> <c- n>fmt</c-><c- o>::</c-><c- n>format_error</c-><c- p>(</c-><c- s>"fwrite error"</c-><c- p>);</c->
<c- p>}</c->

<c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-><c- p>...</c-> <c- n>T</c-><c- o>></c->
<c- b>void</c-> <c- n>print_string</c-><c- p>(</c-><c- n>fmt</c-><c- o>::</c-><c- n>format_string</c-><c- o>&lt;</c-><c- n>T</c-><c- p>...</c-><c- o>></c-> <c- n>fmt</c-><c- p>,</c-> <c- n>T</c-><c- o>&amp;&amp;</c-><c- p>...</c-> <c- n>args</c-><c- p>)</c-> <c- p>{</c->
  <c- n>vprint_string</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>fmt</c-><c- o>::</c-><c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...));</c->
<c- p>}</c->

<c- b>void</c-> <c- n>print_string</c-><c- p>(</c-><c- n>benchmark</c-><c- o>::</c-><c- n>State</c-><c- o>&amp;</c-> <c- n>s</c-><c- p>)</c-> <c- p>{</c->
  <c- k>while</c-> <c- p>(</c-><c- n>s</c-><c- p>.</c-><c- n>KeepRunning</c-><c- p>())</c-> <c- p>{</c->
    <c- n>print_string</c-><c- p>(</c-><c- s>"The answer to life, the universe, and everything is {}.</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- mi>42</c-><c- p>);</c->
  <c- p>}</c->
<c- p>}</c->
<c- n>BENCHMARK</c-><c- p>(</c-><c- n>print_string</c-><c- p>);</c->

<c- b>void</c-> <c- nf>vprint_stack</c-><c- p>(</c-><c- n>fmt</c-><c- o>::</c-><c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>fmt</c-><c- o>::</c-><c- n>format_args</c-> <c- n>args</c-><c- p>)</c-> <c- p>{</c->
  <c- k>auto</c-> <c- n>buf</c-> <c- o>=</c-> <c- n>fmt</c-><c- o>::</c-><c- n>memory_buffer</c-><c- p>();</c->
  <c- n>fmt</c-><c- o>::</c-><c- n>vformat_to</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>back_inserter</c-><c- p>(</c-><c- n>buf</c-><c- p>),</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>);</c->
  <c- b>int</c-> <c- n>result</c-> <c- o>=</c-> <c- n>fwrite</c-><c- p>(</c-><c- n>buf</c-><c- p>.</c-><c- n>data</c-><c- p>(),</c-> <c- mi>1</c-><c- p>,</c-> <c- n>buf</c-><c- p>.</c-><c- n>size</c-><c- p>(),</c-> <c- n>stdout</c-><c- p>);</c->
  <c- k>if</c-> <c- p>(</c-><c- n>result</c-> <c- o>&lt;</c-> <c- n>buf</c-><c- p>.</c-><c- n>size</c-><c- p>())</c-> <c- k>throw</c-> <c- n>fmt</c-><c- o>::</c-><c- n>format_error</c-><c- p>(</c-><c- s>"fwrite error"</c-><c- p>);</c->
<c- p>}</c->

<c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-><c- p>...</c-> <c- n>T</c-><c- o>></c->
<c- b>void</c-> <c- n>print_stack</c-><c- p>(</c-><c- n>fmt</c-><c- o>::</c-><c- n>format_string</c-><c- o>&lt;</c-><c- n>T</c-><c- p>...</c-><c- o>></c-> <c- n>fmt</c-><c- p>,</c-> <c- n>T</c-><c- o>&amp;&amp;</c-><c- p>...</c-> <c- n>args</c-><c- p>)</c-> <c- p>{</c->
  <c- n>vprint_stack</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>fmt</c-><c- o>::</c-><c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...));</c->
<c- p>}</c->

<c- b>void</c-> <c- n>print_stack</c-><c- p>(</c-><c- n>benchmark</c-><c- o>::</c-><c- n>State</c-><c- o>&amp;</c-> <c- n>s</c-><c- p>)</c-> <c- p>{</c->
  <c- k>while</c-> <c- p>(</c-><c- n>s</c-><c- p>.</c-><c- n>KeepRunning</c-><c- p>())</c-> <c- p>{</c->
    <c- n>print_stack</c-><c- p>(</c-><c- s>"The answer to life, the universe, and everything is {}.</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- mi>42</c-><c- p>);</c->
  <c- p>}</c->
<c- p>}</c->
<c- n>BENCHMARK</c-><c- p>(</c-><c- n>print_stack</c-><c- p>);</c->

<c- b>void</c-> <c- nf>print_direct</c-><c- p>(</c-><c- n>benchmark</c-><c- o>::</c-><c- n>State</c-><c- o>&amp;</c-> <c- n>s</c-><c- p>)</c-> <c- p>{</c->
  <c- k>while</c-> <c- p>(</c-><c- n>s</c-><c- p>.</c-><c- n>KeepRunning</c-><c- p>())</c->
    <c- n>fmt</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"The answer to life, the universe, and everything is {}.</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- mi>42</c-><c- p>);</c->
<c- p>}</c->
<c- n>BENCHMARK</c-><c- p>(</c-><c- n>print_direct</c-><c- p>);</c->

<c- n>BENCHMARK_MAIN</c-><c- p>();</c->
</pre>
   <p>Here <code class="highlight"><c- n>print_string</c-></code> formats into a temporary string, <code class="highlight"><c- n>print_stack</c-></code> formats into
a buffer allocated on stack and <code class="highlight"><c- n>print_direct</c-></code> formats directly into the C
stream buffer under a lock. <code class="highlight"><c- n>printf</c-></code> is included for comparison.</p>
   <p>The benchmark was compiled with Apple clang version 15.0.0 (clang-1500.1.0.2.5)
with <code class="highlight"><c- o>-</c-><c- n>O3</c-> <c- o>-</c-><c- n>DNDEBUG</c-></code> and run on macOS 14.2.1 with M1 Pro CPU. Below are the
results:</p>
<pre class="highlight"><c- n>Run</c-> <c- n>on</c-> <c- p>(</c-><c- mi>8</c-> <c- n>X</c-> <c- mi>24</c-> <c- n>MHz</c-> <c- n>CPU</c-> <c- n>s</c-><c- p>)</c->
<c- n>CPU</c-> <c- n>Caches</c-><c- o>:</c->
  <c- n>L1</c-> <c- n>Data</c-> <c- mi>64</c-> <c- n>KiB</c->
  <c- n>L1</c-> <c- n>Instruction</c-> <c- mi>128</c-> <c- n>KiB</c->
  <c- n>L2</c-> <c- n>Unified</c-> <c- mi>4096</c-> <c- n>KiB</c-> <c- p>(</c-><c- n>x8</c-><c- p>)</c->
<c- n>Load</c-> <c- n>Average</c-><c- o>:</c-> <c- mf>5.03</c-><c- p>,</c-> <c- mf>3.99</c-><c- p>,</c-> <c- mf>3.89</c->
<c- o>-------------------------------------------------------</c->
<c- n>Benchmark</c->             <c- n>Time</c->             <c- n>CPU</c->   <c- n>Iterations</c->
<c- o>-------------------------------------------------------</c->
<c- n>printf</c->             <c- mf>81.8</c-> <c- n>ns</c->         <c- mf>81.5</c-> <c- n>ns</c->      <c- mi>8496899</c->
<c- n>print_string</c->       <c- mf>88.5</c-> <c- n>ns</c->         <c- mf>88.2</c-> <c- n>ns</c->      <c- mi>7993240</c->
<c- n>print_stack</c->        <c- mf>63.8</c-> <c- n>ns</c->         <c- mf>61.9</c-> <c- n>ns</c->     <c- mi>11524151</c->
<c- n>print_direct</c->       <c- mf>51.3</c-> <c- n>ns</c->         <c- mf>51.0</c-> <c- n>ns</c->     <c- mi>13846580</c->
</pre>
   <p>Note that estimated CPU frequency is incorrect.</p>
   <p>On Linux (Ubuntu 22.04.3 LTS) with gcc 11.4.0, glibc/libstdc++ and Intel Core
i9-9900K CPU the results are similar except that <code class="highlight"><c- n>printf</c-></code> is slightly faster
than <code class="highlight"><c- n>print</c-></code> with the stack-allocated buffer optimization:</p>
<pre class="highlight"><c- n>Run</c-> <c- n>on</c-> <c- p>(</c-><c- mi>16</c-> <c- n>X</c-> <c- mi>3600</c-> <c- n>MHz</c-> <c- n>CPU</c-> <c- n>s</c-><c- p>)</c->
<c- n>CPU</c-> <c- n>Caches</c-><c- o>:</c->
  <c- n>L1</c-> <c- n>Data</c-> <c- mi>32</c-> <c- n>KiB</c-> <c- p>(</c-><c- n>x8</c-><c- p>)</c->
  <c- n>L1</c-> <c- n>Instruction</c-> <c- mi>32</c-> <c- n>KiB</c-> <c- p>(</c-><c- n>x8</c-><c- p>)</c->
  <c- n>L2</c-> <c- n>Unified</c-> <c- mi>256</c-> <c- n>KiB</c-> <c- p>(</c-><c- n>x8</c-><c- p>)</c->
  <c- n>L3</c-> <c- n>Unified</c-> <c- mi>16384</c-> <c- n>KiB</c-> <c- p>(</c-><c- n>x1</c-><c- p>)</c->
<c- n>Load</c-> <c- n>Average</c-><c- o>:</c-> <c- mf>0.00</c-><c- p>,</c-> <c- mf>0.00</c-><c- p>,</c-> <c- mf>0.00</c->
<c- o>-------------------------------------------------------</c->
<c- n>Benchmark</c->             <c- n>Time</c->             <c- n>CPU</c->   <c- n>Iterations</c->
<c- o>-------------------------------------------------------</c->
<c- n>printf</c->             <c- mf>52.1</c-> <c- n>ns</c->         <c- mf>52.1</c-> <c- n>ns</c->     <c- mi>13386398</c->
<c- n>print_string</c->       <c- mf>65.7</c-> <c- n>ns</c->         <c- mf>65.7</c-> <c- n>ns</c->     <c- mi>10674838</c->
<c- n>print_stack</c->        <c- mf>55.8</c-> <c- n>ns</c->         <c- mf>55.8</c-> <c- n>ns</c->     <c- mi>12535414</c->
<c- n>print_direct</c->       <c- mf>46.3</c-> <c- n>ns</c->         <c- mf>46.3</c-> <c- n>ns</c->     <c- mi>15087266</c->
</pre>
   <p>Direct output is 42-72% faster than writing to a temporary string and 21-24%
faster than writing to a stack-allocated buffer on this benchmark.</p>
   <p>Preliminary testing in libstc++ showed ~25% improvement compared to the
existing implementation.</p>
   <h2 class="heading settled" data-level="11" id="impl"><span class="secno">11. </span><span class="content">Implementation</span><a class="self-link" href="#impl"></a></h2>
   <p>This proposal has been implemented in the open-source {fmt} library (<a data-link-type="biblio" href="#biblio-fmt" title="The {fmt} library">[FMT]</a>)
bringing major performance improvements.</p>
   <h2 class="heading settled" data-level="12" id="wording"><span class="secno">12. </span><span class="content">Wording</span><a class="self-link" href="#wording"></a></h2>
   <p>Update the value of the feature-testing macro <code class="highlight"><c- n>__cpp_lib_print</c-></code> to the date of
adoption in <a href="https://eel.is/c++draft/version.syn">[version.syn</a>].</p>
   <p>Modify <a href="https://eel.is/c++draft/format.syn">[format.syn</a>] as
indicated:</p>
<pre class="highlight"><c- c1>// [format.formatter], formatter</c->
<c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>T</c-><c- p>,</c-> <c- k>class</c-> <c- nc>charT</c-> <c- o>=</c-> <c- b>char</c-><c- o>></c-> <c- k>struct</c-> <c- nc>formatter</c-><c- p>;</c->

<ins><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>T</c-><c- o>></c->
  <c- k>constexpr</c-> <c- b>bool</c-> <c- n>enable_nonlocking_formatter_optimization</c-> <c- o>=</c-> false<c- p>;</c-></ins>

<c- p>...</c->
</pre>
   <p>Add a new clause [format.formatter.locking] to <a href="https://eel.is/c++draft/format.formatter">[format.formatter</a>]:</p>
<pre class="highlight"><ins>
<c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>T</c-><c- o>></c->
  <c- k>constexpr</c-> <c- b>bool</c-> <c- n>enable_nonlocking_formatter_optimization</c-> <c- o>=</c-> false<c- p>;</c->
</ins>
</pre>
   <ins> <em>Remarks</em>: Pursuant to <a href="https://eel.is/c++draft/format.formatter">[namespace.std</a>], users may specialize <code class="highlight"><c- n>enable_nonlocking_formatter_optimization</c-></code> for cv-unqualified program-defined
types. Such specializations shall be usable in constant expressions
(<a href="https://eel.is/c++draft/format.formatter">[expr.const</a>]) and have type <code class="highlight"><c- k>const</c-> <c- b>bool</c-></code>. </ins>
   <p>Modify <a href="https://eel.is/c++draft/print.fun">[print.fun</a>] as indicated:</p>
   <p>...</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-><c- p>...</c-> <c- n>Args</c-><c- o>></c->
  <c- b>void</c-> <c- n>print</c-><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>format_string</c-><c- o>&lt;</c-><c- n>Args</c-><c- p>...</c-><c- o>></c-> <c- n>fmt</c-><c- p>,</c-> <c- n>Args</c-><c- o>&amp;&amp;</c-><c- p>...</c-> <c- n>args</c-><c- p>);</c->
</pre>
   <p><em>Effects</em>:</p>
   <ins> Let <code class="highlight"><c- n>locksafe</c-></code> be <code class="highlight"><c- p>(</c-><c- n>enable_nonlocking_formatter_optimization</c-><c- o>&lt;</c-><c- n>remove_cvref_t</c-><c- o>&lt;</c-><c- n>Args</c-><c- o>>></c-> <c- o>&amp;&amp;</c-> <c- p>...)</c-></code>. </ins>
   <p>If the ordinary literal encoding (<a href="https://eel.is/c++draft/version.syn">[lex.charset</a>]) is UTF-8, equivalent to:</p>
<pre class="highlight"><del><c- n>vprint_unicode</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>fmt</c-><c- p>.</c-><c- n>str</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...));</c-></del>
<ins>
<c- n>locksafe</c-> <c- o>?</c->
  <c- n>vprint_unicode_locking</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>fmt</c-><c- p>.</c-><c- n>str</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...))</c-> <c- o>:</c->
  <c- n>vprint_unicode</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>fmt</c-><c- p>.</c-><c- n>str</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...));</c-></ins>
</pre>
   <p>Otherwise, equivalent to:</p>
<pre class="highlight"><del><c- n>vprint_nonunicode</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>fmt</c-><c- p>.</c-><c- n>str</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...));</c-></del>
<ins>
<c- n>locksafe</c-> <c- o>?</c->
  <c- n>vprint_nonunicode_locking</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>fmt</c-><c- p>.</c-><c- n>str</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...))</c-> <c- o>:</c->
  <c- n>vprint_nonunicode</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>fmt</c-><c- p>.</c-><c- n>str</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>args</c-><c- p>...));</c->
</ins>
</pre>
   <p>...</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-><c- p>...</c-> <c- n>Args</c-><c- o>></c->
  <c- b>void</c-> <c- n>println</c-><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>format_string</c-><c- o>&lt;</c-><c- n>Args</c-><c- p>...</c-><c- o>></c-> <c- n>fmt</c-><c- p>,</c-> <c- n>Args</c-><c- o>&amp;&amp;</c-><c- p>...</c-> <c- n>args</c-><c- p>);</c->
</pre>
   <p><em>Effects</em>: Equivalent to:</p>
<pre class="highlight"><del><c- n>print</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- s>"{}</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- n>format</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>forward</c-><c- o>&lt;</c-><c- n>Args</c-><c- o>></c-><c- p>(</c-><c- n>args</c-><c- p>)...));</c-></del>
<ins><c- n>print</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- n>runtime_format</c-><c- p>(</c-><c- n>string</c-><c- p>(</c-><c- n>fmt</c-><c- p>.</c-><c- n>get</c-><c- p>())</c-> <c- o>+</c-> <c- sc>'\n'</c-><c- p>),</c-> <c- n>std</c-><c- o>::</c-><c- n>forward</c-><c- o>&lt;</c-><c- n>Args</c-><c- o>></c-><c- p>(</c-><c- n>args</c-><c- p>)...);</c-></ins>
</pre>
<pre class="highlight"><ins><c- b>void</c-> <c- nf>vprint_unicode</c-><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>format_args</c-> <c- n>args</c-><c- p>);</c-></ins>
</pre>
   <ins> <em>Effects</em>: Equivalent to: </ins>
<pre class="highlight"><ins><c- n>string</c-> <c- n>out</c-> <c- o>=</c-> <c- n>vformat</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>);</c->
<c- n>vprint_unicode_locking</c-><c- p>(</c-><c- n>stream</c-><c- p>,</c-> <c- s>"{}"</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>out</c-><c- p>));</c-></ins>
</pre>
<pre class="highlight"><c- b>void</c-> <c- nf>vprint_unicode</c-><ins><c- nf>_locking</c-></ins><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>format_args</c-> <c- n>args</c-><c- p>);</c->
</pre>
   <p><em>Preconditions</em>: <code class="highlight"><c- n>stream</c-></code> is a valid pointer to an output C stream.</p>
   <p>
    <em>Effects</em>: 
    <del>The function initializes an automatic variable via</del>
   </p>
<pre class="highlight"><del><c- n>string</c-> <c- n>out</c-> <c- o>=</c-> <c- n>vformat</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>);</c-></del>
</pre>
   <ins>Locks <code class="highlight"><c- n>stream</c-></code>.</ins>
   <p>
    <ins> SEE ALSO: ISO/IEC 9899:2018, 7.21.2 </ins>
   </p>
   <p>
    <ins>Let <code class="highlight"><c- n>out</c-></code> denote the character representation of formatting arguments
provided by <code class="highlight"><c- n>args</c-></code> formatted according to specifications given in <code class="highlight"><c- n>fmt</c-></code>.</ins>
   </p>
   <p>
    If <code class="highlight"><c- n>stream</c-></code> refers to a terminal capable of displaying Unicode, writes <code class="highlight"><c- n>out</c-></code> to
the terminal using the native Unicode API; if <code class="highlight"><c- n>out</c-></code> contains invalid code units,
the behavior is undefined and implementations are encouraged to diagnose it.
Otherwise writes <code class="highlight"><c- n>out</c-></code> to <code class="highlight"><c- n>stream</c-></code> unchanged. If the native Unicode API is used,
the function flushes <code class="highlight"><c- n>stream</c-></code> before writing <code class="highlight"><c- n>out</c-></code>. 
    <ins>Unconditionally unlocks <code class="highlight"><c- n>stream</c-></code> on function exit.</ins>
   </p>
   <p>...</p>
<pre class="highlight"><ins><c- b>void</c-> <c- nf>vprint_nonunicode</c-><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>format_args</c-> <c- n>args</c-><c- p>);</c-></ins>
</pre>
   <ins> <em>Effects</em>: Equivalent to: </ins>
<pre class="highlight"><ins><c- n>string</c-> <c- n>out</c-> <c- o>=</c-> <c- n>vformat</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>);</c->
<c- n>vprint_nonunicode_locking</c-><c- p>(</c-><c- s>"{}"</c-><c- p>,</c-> <c- n>make_format_args</c-><c- p>(</c-><c- n>out</c-><c- p>));</c-></ins>
</pre>
<pre class="highlight"><c- b>void</c-> <c- nf>vprint_nonunicode</c-><ins><c- nf>_locking</c-></ins><c- p>(</c-><c- b>FILE</c-><c- o>*</c-> <c- n>stream</c-><c- p>,</c-> <c- n>string_view</c-> <c- n>fmt</c-><c- p>,</c-> <c- n>format_args</c-> <c- n>args</c-><c- p>);</c->
</pre>
   <p><em>Preconditions</em>: <code class="highlight"><c- n>stream</c-></code> is a valid pointer to an output C stream.</p>
   <p>
    <em>Effects</em>: 
    <del>Writes the result of <code class="highlight"><c- n>vformat</c-><c- p>(</c-><c- n>fmt</c-><c- p>,</c-> <c- n>args</c-><c- p>)</c-></code> to <code class="highlight"><c- n>stream</c-></code>.</del>
   </p>
   <ins>While holding the lock on <code class="highlight"><c- n>stream</c-></code>, writes the character representation of
formatting arguments provided by <code class="highlight"><c- n>args</c-></code> formatted according to specifications
given in <code class="highlight"><c- n>fmt</c-></code> to <code class="highlight"><c- n>stream</c-></code>.</ins>
   <p><em>Throws</em>: Any exception thrown by the call to <code class="highlight"><c- n>vformat</c-></code> (<a href="https://eel.is/c++draft/format.err.report">[format.err.report</a>]). <code class="highlight"><c- n>system_error</c-></code> if writing to <code class="highlight"><c- n>stream</c-></code> fails. May throw <code class="highlight"><c- n>bad_alloc</c-></code>.</p>
   <p>...</p>
   <p>Modify <a href="https://eel.is/c++draft/format.formatter.spec">[format.formatter.spec</a>]
as indicated:</p>
   <p>...</p>
   <p>Let <code class="highlight"><c- n>charT</c-></code> be either <code class="highlight"><c- b>char</c-></code> or <code class="highlight"><c- b>wchar_t</c-></code>. Each specialization of <code class="highlight"><c- n>formatter</c-></code> is
either enabled or disabled, as described below. A <em>debug-enabled</em> specialization
of <code class="highlight"><c- n>formatter</c-></code> additionally provides a public, constexpr, non-static member
function <code class="highlight"><c- n>set_debug_format</c-><c- p>()</c-></code> which modifies the state of the <code class="highlight"><c- n>formatter</c-></code> to be
as if the type of the <em>std-format-spec</em> parsed by the last call to parse were <code class="highlight"><c- o>?</c-></code>. Each header that declares the template <code class="highlight"><c- n>formatter</c-></code> provides the following
enabled specializations:</p>
   <ul>
    <li data-md>
     <p>The debug-enabled specializations</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;></c-> <c- k>struct</c-> <c- nc>formatter</c-><c- o>&lt;</c-><c- b>char</c-><c- p>,</c-> <c- b>char</c-><c- o>></c-><c- p>;</c->
<c- k>template</c-><c- o>&lt;></c-> <c- k>struct</c-> <c- nc>formatter</c-><c- o>&lt;</c-><c- b>char</c-><c- p>,</c-> <c- b>wchar_t</c-><c- o>></c-><c- p>;</c->
<c- k>template</c-><c- o>&lt;></c-> <c- k>struct</c-> <c- nc>formatter</c-><c- o>&lt;</c-><c- b>wchar_t</c-><c- p>,</c-> <c- b>wchar_t</c-><c- o>></c-><c- p>;</c->
</pre>
     <p>...</p>
   </ul>
   <p>The parse member functions of these formatters interpret the format
specification as a <em>std-format-spec</em> as described in <a href="https://eel.is/c++draft/format.string.std">[format.string.std</a>].</p>
   <ins> In addition, for each type <code class="highlight"><c- n>T</c-></code> for which a <code class="highlight"><c- n>formatter</c-></code> specialization is
provided above, each of the headers provides the following specialization:</ins>
<pre class="highlight"><ins><c- k>template</c-><c- o>&lt;></c->
<c- kr>inline</c-> <c- k>constexpr</c-> <c- b>bool</c-> <c- n>enable_nonlocking_formatter_optimization</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- o>=</c-> true<c- p>;</c-></ins>
</pre>
   <p>[<em>Note 1</em>: Specializations such as <code class="highlight"><c- n>formatter</c-><c- o>&lt;</c-><c- b>wchar_t</c-><c- p>,</c-> <c- b>char</c-><c- o>></c-></code> and <code class="highlight"><c- n>formatter</c-><c- o>&lt;</c-><c- k>const</c-> <c- b>char</c-><c- o>*</c-><c- p>,</c-> <c- b>wchar_t</c-><c- o>></c-></code> that would require implicit multibyte /
wide string or character conversion are disabled. — end note]</p>
   <p>...</p>
   <h2 class="heading settled" data-level="13" id="ack"><span class="secno">13. </span><span class="content">Acknowledgements</span><a class="self-link" href="#ack"></a></h2>
   <p>Thanks to Jonathan Wakely for implementing the proposal in libstdc++,
providing benchmark results and suggesting various improvements to the paper.</p>
   <p>Thanks to Ben Craig for proposing to make user-defined formatters opt into the
new behavior to prevent potential deadlocks.</p>
  </main>
<script>
(function() {
  "use strict";
  var collapseSidebarText = '<span aria-hidden="true">←</span> '
                          + '<span>Collapse Sidebar</span>';
  var expandSidebarText   = '<span aria-hidden="true">→</span> '
                          + '<span>Pop Out Sidebar</span>';
  var tocJumpText         = '<span aria-hidden="true">↑</span> '
                          + '<span>Jump to Table of Contents</span>';

  var sidebarMedia = window.matchMedia('screen and (min-width: 78em)');
  var autoToggle   = function(e){ toggleSidebar(e.matches) };
  if(sidebarMedia.addListener) {
    sidebarMedia.addListener(autoToggle);
  }

  function toggleSidebar(on) {
    if (on == undefined) {
      on = !document.body.classList.contains('toc-sidebar');
    }

    /* Don't scroll to compensate for the ToC if we're above it already. */
    var headY = 0;
    var head = document.querySelector('.head');
    if (head) {
      // terrible approx of "top of ToC"
      headY += head.offsetTop + head.offsetHeight;
    }
    var skipScroll = window.scrollY < headY;

    var toggle = document.getElementById('toc-toggle');
    var tocNav = document.getElementById('toc');
    if (on) {
      var tocHeight = tocNav.offsetHeight;
      document.body.classList.add('toc-sidebar');
      document.body.classList.remove('toc-inline');
      toggle.innerHTML = collapseSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, 0 - tocHeight);
      }
      tocNav.focus();
      sidebarMedia.addListener(autoToggle); // auto-collapse when out of room
    }
    else {
      document.body.classList.add('toc-inline');
      document.body.classList.remove('toc-sidebar');
      toggle.innerHTML = expandSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, tocNav.offsetHeight);
      }
      if (toggle.matches(':hover')) {
        /* Unfocus button when not using keyboard navigation,
           because I don't know where else to send the focus. */
        toggle.blur();
      }
    }
  }

  function createSidebarToggle() {
    /* Create the sidebar toggle in JS; it shouldn't exist when JS is off. */
    var toggle = document.createElement('a');
      /* This should probably be a button, but appearance isn't standards-track.*/
    toggle.id = 'toc-toggle';
    toggle.class = 'toc-toggle';
    toggle.href = '#toc';
    toggle.innerHTML = collapseSidebarText;

    sidebarMedia.addListener(autoToggle);
    var toggler = function(e) {
      e.preventDefault();
      sidebarMedia.removeListener(autoToggle); // persist explicit off states
      toggleSidebar();
      return false;
    }
    toggle.addEventListener('click', toggler, false);


    /* Get <nav id=toc-nav>, or make it if we don't have one. */
    var tocNav = document.getElementById('toc-nav');
    if (!tocNav) {
      tocNav = document.createElement('p');
      tocNav.id = 'toc-nav';
      /* Prepend for better keyboard navigation */
      document.body.insertBefore(tocNav, document.body.firstChild);
    }
    /* While we're at it, make sure we have a Jump to Toc link. */
    var tocJump = document.getElementById('toc-jump');
    if (!tocJump) {
      tocJump = document.createElement('a');
      tocJump.id = 'toc-jump';
      tocJump.href = '#toc';
      tocJump.innerHTML = tocJumpText;
      tocNav.appendChild(tocJump);
    }

    tocNav.appendChild(toggle);
  }

  var toc = document.getElementById('toc');
  if (toc) {
    createSidebarToggle();
    toggleSidebar(sidebarMedia.matches);

    /* If the sidebar has been manually opened and is currently overlaying the text
       (window too small for the MQ to add the margin to body),
       then auto-close the sidebar once you click on something in there. */
    toc.addEventListener('click', function(e) {
      if(e.target.tagName.toLowerCase() == "a" && document.body.classList.contains('toc-sidebar') && !sidebarMedia.matches) {
        toggleSidebar(false);
      }
    }, false);
  }
  else {
    console.warn("Can't find Table of Contents. Please use <nav id='toc'> around the ToC.");
  }

  /* Wrap tables in case they overflow */
  var tables = document.querySelectorAll(':not(.overlarge) > table.data, :not(.overlarge) > table.index');
  var numTables = tables.length;
  for (var i = 0; i < numTables; i++) {
    var table = tables[i];
    var wrapper = document.createElement('div');
    wrapper.className = 'overlarge';
    table.parentNode.insertBefore(wrapper, table);
    wrapper.appendChild(table);
  }

})();
</script>
  <h2 class="no-num no-ref heading settled" id="references"><span class="content">References</span><a class="self-link" href="#references"></a></h2>
  <h3 class="no-num no-ref heading settled" id="informative"><span class="content">Informative References</span><a class="self-link" href="#informative"></a></h3>
  <dl>
   <dt id="biblio-fmt">[FMT]
   <dd>Victor Zverovich; et al. <a href="https://github.com/fmtlib/fmt"><cite>The {fmt} library</cite></a>. URL: <a href="https://github.com/fmtlib/fmt">https://github.com/fmtlib/fmt</a>
   <dt id="biblio-google-bench">[GOOGLE-BENCH]
   <dd><a href="https://github.com/google/benchmark"><cite>Google Benchmark: A microbenchmark support library</cite></a>. URL: <a href="https://github.com/google/benchmark">https://github.com/google/benchmark</a>
   <dt id="biblio-lwg4042">[LWG4042]
   <dd><a href="https://cplusplus.github.io/LWG/issue4042"><cite>LWG Issue 4042: `std::print` should permit an efficient implementation</cite></a>. URL: <a href="https://cplusplus.github.io/LWG/issue4042">https://cplusplus.github.io/LWG/issue4042</a>
   <dt id="biblio-n2310-streams">[N2310-STREAMS]
   <dd><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf#page=233"><cite>7.21.2 Streams. ISO/IEC 9899:202x. Programming languages — C</cite></a>. URL: <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf#page=233">https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf#page=233</a>
   <dt id="biblio-p2093">[P2093]
   <dd>Victor Zverovich. <a href="https://wg21.link/p2093"><cite>Formatted output</cite></a>. URL: <a href="https://wg21.link/p2093">https://wg21.link/p2093</a>
   <dt id="biblio-stdio-lock">[STDIO-LOCK]
   <dd><a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html"><cite>The Open Group Base Specifications Issue 7, 2018 edition. IEEE Std 1003.1-2017. flockfile, ftrylockfile, funlockfile - stdio locking functions</cite></a>. URL: <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html">https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html</a>
  </dl>