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

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

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

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

code.sourceCode > span { display: inline; }
</style>
  <link href="data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Attributes reflection</h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P3385R1</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2024-10-15</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      sg7<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Aurelien Cassagnes<br>&lt;<a href="mailto:aurelien.cassagnes@gmail.com" class="email">aurelien.cassagnes@gmail.com</a>&gt;<br>
      Roman Khoroshikh<br>&lt;<a href="mailto:rkhoroshikh@bloomberg.net" class="email">rkhoroshikh@bloomberg.net</a>&gt;<br>
      Anders Johansson<br>&lt;<a href="mailto:ajohansson12@bloomberg.net" class="email">ajohansson12@bloomberg.net</a>&gt;<br>
    </td>
  </tr>
</table>
</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#revision-history" id="toc-revision-history"><span class="toc-section-number">1</span> Revision
history<span></span></a></li>
<li><a href="#introduction" id="toc-introduction"><span class="toc-section-number">2</span> Introduction<span></span></a>
<ul>
<li><a href="#earlier-work" id="toc-earlier-work"><span class="toc-section-number">2.1</span> Earlier work<span></span></a></li>
<li><a href="#scope" id="toc-scope"><span class="toc-section-number">2.2</span> Scope<span></span></a></li>
<li><a href="#optionality-rule" id="toc-optionality-rule"><span class="toc-section-number">2.3</span> Optionality
rule<span></span></a></li>
</ul></li>
<li><a href="#proposed-features" id="toc-proposed-features"><span class="toc-section-number">3</span> Proposed Features<span></span></a>
<ul>
<li><a href="#stdmetainfo" id="toc-stdmetainfo"><span class="toc-section-number">3.1</span>
std::meta::info<span></span></a></li>
<li><a href="#reflection-operator" id="toc-reflection-operator"><span class="toc-section-number">3.2</span> Reflection
operator<span></span></a></li>
<li><a href="#splicers" id="toc-splicers"><span class="toc-section-number">3.3</span> Splicers<span></span></a></li>
<li><a href="#metafunctions" id="toc-metafunctions"><span class="toc-section-number">3.4</span>
Metafunctions<span></span></a></li>
<li><a href="#queries" id="toc-queries"><span class="toc-section-number">3.5</span> Queries<span></span></a></li>
</ul></li>
<li><a href="#proposed-wording" id="toc-proposed-wording"><span class="toc-section-number">4</span> Proposed wording<span></span></a>
<ul>
<li><a href="#language" id="toc-language"><span class="toc-section-number">4.1</span> Language<span></span></a></li>
<li><a href="#library" id="toc-library"><span class="toc-section-number">4.2</span> Library<span></span></a></li>
</ul></li>
<li><a href="#future-direction" id="toc-future-direction"><span class="toc-section-number">5</span> Future direction<span></span></a>
<ul>
<li><a href="#scoped-attributes" id="toc-scoped-attributes"><span class="toc-section-number">5.1</span> Scoped
attributes<span></span></a></li>
<li><a href="#implementation" id="toc-implementation"><span class="toc-section-number">5.2</span>
Implementation<span></span></a></li>
</ul></li>
<li><a href="#conclusion" id="toc-conclusion"><span class="toc-section-number">6</span> Conclusion<span></span></a></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">7</span> References<span></span></a></li>
</ul>
</div>
<h1 data-number="1" id="revision-history"><span class="header-section-number">1</span> Revision history<a href="#revision-history" class="self-link"></a></h1>
<p>Since <span class="citation" data-cites="P3385R0">[<a href="https://wg21.link/p3385r0" role="doc-biblioref">P3385R0</a>]</span></p>
<ul>
<li>Rebase on <span class="citation" data-cites="P2996R5">[<a href="https://wg21.link/p2996r5" role="doc-biblioref">P2996R5</a>]</span></li>
<li>Reword ignorability section to mention set of rules</li>
<li>Discuss argument clause in <code class="sourceCode cpp">std<span class="op">::</span>meta<span class="op">::</span>info</code>
equal operator</li>
<li>Discuss appertaining to null statement in reflect expression</li>
</ul>
<h1 data-number="2" id="introduction"><span class="header-section-number">2</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>Attributes are used to a great extent, and there likely will be new
attributes added as the language evolves.<br />
As reflection makes its way into our standard, what is missing is a way
for generic code to look into the attributes appertaining to an entity.
That is what this proposal aims to tackle.<br />
</p>
<p>A motivating example is the following</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">enum</span> <span class="kw">class</span> Result <span class="op">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>        success,</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        warn,</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        fail,</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> StrictNormalize <span class="op">{</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> <span class="kw">operator</span><span class="op">()</span> <span class="op">(</span>Result status<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> status <span class="op">==</span> Result<span class="op">::</span>success;</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> F<span class="op">&gt;</span> </span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at"> </span><span class="op">[:</span><span class="at"> </span><span class="op">^</span><span class="at">F </span><span class="op">:]</span><span class="at"> </span><span class="op">]]</span> <span class="co">// expand into [[ nodiscard ]]</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> transform<span class="op">(</span><span class="kw">auto</span><span class="op">...</span> args<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> F<span class="op">()(</span>args<span class="op">...)</span>;</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>        transform<span class="op">&lt;</span>StrictNormalize<span class="op">&gt;(</span>Result<span class="op">::</span>success<span class="op">)</span>; <span class="co">// warning on &quot;nodiscard&quot;</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>        <span class="dt">bool</span> isOk <span class="op">=</span> transform<span class="op">&lt;</span>StrictNormalize<span class="op">&gt;(</span>Result<span class="op">::</span>success<span class="op">)</span>; <span class="co">// OK</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>Here <code class="sourceCode cpp">transform</code> looks into the
attributes attached to the callable
<code class="sourceCode cpp">F</code>, and recovers the <code class="sourceCode cpp"><span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span></code>
attribute that originally appertained to
<code class="sourceCode cpp">StrictNormalize</code> declaration.<br />
</p>
<p>We expect a number of applications for attribute introspection to
happen in the context of code injection <span class="citation" data-cites="P2237R0">[<a href="https://wg21.link/p2237r0" role="doc-biblioref">P2237R0</a>]</span>, where, for example, one may
want to skip over <code class="sourceCode cpp"><span class="op">[[</span><span class="at">deprecated</span><span class="op">]]</span></code>
members. The following example demonstrates skipping over deprecated
members:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> User <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">deprecated</span><span class="op">]]</span> std<span class="op">::</span>string name;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>string uuidv5;</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">deprecated</span><span class="op">]]</span> std<span class="op">::</span>string country;</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>string countryIsoCode;</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> std<span class="op">::</span>vector<span class="op">&lt;</span>std<span class="op">::</span>meta<span class="op">::</span>info<span class="op">&gt;</span> liveMembers<span class="op">(</span><span class="kw">const</span> T<span class="op">&amp;</span> user<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>vector<span class="op">&lt;</span>std<span class="op">::</span>meta<span class="op">::</span>info<span class="op">&gt;</span> liveMembers;</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> deprecatedAttribute <span class="op">=</span> std<span class="op">::</span>meta<span class="op">::</span>attributes_of<span class="op">(^[[</span><span class="at">deprecated</span><span class="op">]])[</span><span class="dv">0</span><span class="op">]</span>;</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> keepLive <span class="op">=</span> <span class="op">[&amp;]</span> <span class="op">&lt;</span><span class="kw">auto</span> r<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>      <span class="cf">if</span> <span class="op">(!</span>std<span class="op">::</span>ranges<span class="op">::</span>any_of<span class="op">(</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>        attributes_of<span class="op">(^</span>T<span class="op">)</span>,</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>        <span class="op">[</span>deprecatedAttribute<span class="op">]</span> <span class="op">(</span><span class="kw">auto</span> meta<span class="op">)</span> <span class="op">{</span> meta <span class="op">==</span> deprecatedAttributes; <span class="op">}</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>      <span class="op">))</span> <span class="op">{</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>        liveMembers<span class="op">.</span>push_back<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span> <span class="cf">for</span> <span class="op">(</span><span class="kw">auto</span> member <span class="op">:</span> std<span class="op">::</span>meta<span class="op">::</span>members_of<span class="op">(^</span>User<span class="op">))</span> <span class="op">{</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>      keepLive<span class="op">(</span>member<span class="op">)</span>;</span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> os;</span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Migrated user will no longer support deprecated fields</span></span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> MigratedUser;</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a>  define_class<span class="op">(^</span>MigratedUser, liveMembers<span class="op">(</span>currentUser<span class="op">))</span>;</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<h2 data-number="2.1" id="earlier-work"><span class="header-section-number">2.1</span> Earlier work<a href="#earlier-work" class="self-link"></a></h2>
<p>While collecting feedback on this draft, we were redirected to <span class="citation" data-cites="P1887R1">[<a href="https://wg21.link/p1887r1" role="doc-biblioref">P1887R1</a>]</span>, a pre existing proposal. In
this paper, the author discusses two topics: ‘user defined attributes’
(also in <span class="citation" data-cites="P2565R0">[<a href="https://wg21.link/p2565r0" role="doc-biblioref">P2565R0</a>]</span>) and reflection over said
attributes. We believe these two topics need not be conflated; both have
intrinsic values on their own. We aim to focus this discussion entirely
on <strong>standard</strong> attributes reflection. Furthermore the
earlier paper has not seen work following the progression of <span class="citation" data-cites="P2996R5">[<a href="https://wg21.link/p2996r5" role="doc-biblioref">P2996R5</a>]</span>, and so we feel this proposal
is in a good place to fill that gap.</p>
<h2 data-number="2.2" id="scope"><span class="header-section-number">2.2</span> Scope<a href="#scope" class="self-link"></a></h2>
<p>Attributes are split into standard and non-standard. This proposal
wishes to limit itself to standard attributes (<span>9.12
<a href="https://wg21.link/dcl.attr">[dcl.attr]</a></span>). We feel
that since it is up to the implementation to define how to handle
non-standard attributes, it would lead to obscure situations that we
don’t claim to tackle here.<br />
A fairly simple (admittedly artificial) example can be built as such:
Given an implementation supporting a non-standard <code class="sourceCode cpp"><span class="op">[[</span><span class="at">privacy</span><span class="op">::</span><span class="at">no_reflection</span><span class="op">]]</span></code>
attributes that suppresses all reflection information appertaining to an
entity, we would have a hard time coming up with a self-consistent
system of rules to start with.<br />
</p>
<p>Henceforth, in this proposal, both ‘attributes’ and ‘standard
attributes’ terms are meant to be equivalentterms. For interested
readers we will re-open that topic in the <a href="#future-direction">future direction</a> section.</p>
<h2 data-number="2.3" id="optionality-rule"><span class="header-section-number">2.3</span> Optionality rule<a href="#optionality-rule" class="self-link"></a></h2>
<p>There is a long standing and confusing discussion around the
ignorability of attributes. We’ll refer the reader to <span class="citation" data-cites="P2552R3">[<a href="https://wg21.link/p2552r3" role="doc-biblioref">P2552R3</a>]</span> for an at-length discussion of
this problem, and especially with regard to what ‘ignorability’ really
means. This proposal agrees with the discussion carried on there and in
<span class="citation" data-cites="CWG2538">[<a href="https://wg21.link/cwg2538" role="doc-biblioref">CWG2538</a>]</span>. We also feel that whether an
implementation decides to semantically ignore a standard attribute
should not matter.<br />
</p>
<p>Another interesting conversation takes place in <span class="citation" data-cites="P3254R0">[<a href="https://wg21.link/p3254r0" role="doc-biblioref">P3254R0</a>]</span>, around the <code class="sourceCode cpp"><span class="op">[[</span><span class="at">no_unique_address</span><span class="op">]]</span></code>
case, which serves again to illustrate that the tension around so called
ignorability should not be considered a novel feature of this
proposal.</p>
<p>What matters more is the following set of rules</p>
<ul>
<li>We should be able to statically reflect on appertaining attributes
even if ignored: Implementations are required to parse and check
syntactic rules related to attributes. Whether they are ignored
semantically later on should not mean that they are removed from the
source code during translation<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</li>
<li>Splicing attributes on a declaration should be undistinguishable
from appertaining those attributes to this declaration.</li>
</ul>
<h1 data-number="3" id="proposed-features"><span class="header-section-number">3</span> Proposed Features<a href="#proposed-features" class="self-link"></a></h1>
<p>We put ourselves in the context of <span class="citation" data-cites="P2996R5">[<a href="https://wg21.link/p2996r5" role="doc-biblioref">P2996R5</a>]</span> for this proposal to be more
illustrative in terms of what is being proposed.</p>
<h2 data-number="3.1" id="stdmetainfo"><span class="header-section-number">3.1</span> std::meta::info<a href="#stdmetainfo" class="self-link"></a></h2>
<p>We propose that attributes be a supported <em>reflectable</em>
property of the expression that is reflected upon. That means value of
type <code class="sourceCode cpp">std<span class="op">::</span>meta<span class="op">::</span>info</code>
should be able to represent an attribute in addition to the currently
supported set.</p>
<h2 data-number="3.2" id="reflection-operator"><span class="header-section-number">3.2</span> Reflection operator<a href="#reflection-operator" class="self-link"></a></h2>
<p>The current proposition for reflection operator grammar does not
cover attributes, i.e., the expression <code class="sourceCode cpp"><span class="op">^[[</span><span class="at">deprecated</span><span class="op">]]</span></code>
is ill-formed. Our proposal advocates to support expression as
following:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> <span class="kw">auto</span> keepAttribute <span class="op">=</span> <span class="op">^[[</span><span class="at">nodiscard</span><span class="op">]]</span>;</span></code></pre></div>
<p>The resulting value is a reflection value embedding relevant info for
the <em>attribute</em> entity; in this case, the
<code class="sourceCode cpp">nodiscard</code> attribute.<br />
If the <em>attribute</em> is not a standard attribute, the expression is
ill-formed.</p>
<h2 data-number="3.3" id="splicers"><span class="header-section-number">3.3</span> Splicers<a href="#splicers" class="self-link"></a></h2>
<p>We propose that the syntax</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at"> </span><span class="op">[:</span><span class="at"> r </span><span class="op">:]</span><span class="at"> </span><span class="op">]]</span></span></code></pre></div>
<p>be supported in contexts where attributes are allowed.</p>
<ul>
<li><code class="sourceCode cpp"><span class="op">[[</span><span class="at"> </span><span class="op">[:</span><span class="at"> r </span><span class="op">:]</span><span class="at"> </span><span class="op">]]</span></code>
produces a potentially empty
<code class="sourceCode cpp"><em>attribute-list</em></code>
corresponding to the attributes reflected via reflection
<code class="sourceCode cpp">r</code>.</li>
</ul>
<p>Note that as it stands now
<code class="sourceCode cpp"><em>attribute-list</em></code>
(<span>9.12.1
<a href="https://wg21.link/dcl.attr.grammar">[dcl.attr.grammar]</a></span>)
does not cover
<code class="sourceCode cpp"><span class="kw">alignas</span></code>. We
understand that this limits potential use of the current proposal but
also comes with difficulty, so this will be discussed in a separate
paper.</p>
<p>A simple example of splicer using expansion statements is as follows.
We create an augmented
<code class="sourceCode cpp"><span class="kw">enum</span></code>
introducing a <code class="sourceCode cpp">begin</code> and
<code class="sourceCode cpp">last</code> enumerator while preserving the
original attributes:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">enum</span> <span class="kw">class</span> ErrorCode <span class="op">{</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>      warn,</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>      fatal,</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at"> </span><span class="op">[:</span><span class="at"> </span><span class="op">^</span><span class="at">ErrorCode </span><span class="op">:]</span><span class="at"> </span><span class="op">]]</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">enum</span> <span class="kw">class</span> ClosedErrorCode <span class="op">{</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>      begin,</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>      <span class="kw">template</span> <span class="cf">for</span> <span class="op">(</span><span class="kw">constexpr</span> <span class="kw">auto</span> e <span class="op">:</span> std<span class="op">::</span>meta<span class="op">::</span>enumerators_of<span class="op">(^</span>ErrorCode<span class="op">))</span> <span class="op">{</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="op">[:</span>e<span class="op">:]</span>,</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>      last,</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>If the attributes produced through introspection violate the rules of
what attributes can appertain to what entity, the program is ill-formed,
as usual.</p>
<h3 data-number="3.3.1" id="attribute-using"><span class="header-section-number">3.3.1</span> Attribute using<a href="#attribute-using" class="self-link"></a></h3>
<p>It is worth pointing out the interaction between
<code class="sourceCode cpp"><em>attribute-using-prefix</em></code> and
splice expression that could lead to unexpected results, such as in the
following example</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> unscopedAttribute <span class="op">=</span> <span class="op">^[[</span><span class="at">nodiscard</span><span class="op">]]</span>;</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at"> </span><span class="kw">using</span><span class="at"> CC</span><span class="op">:</span><span class="at"> debug</span>,<span class="at"> </span><span class="op">[:</span><span class="at"> unscopedAttribute </span><span class="op">:]</span><span class="at"> </span><span class="op">]]</span> <span class="kw">enum</span> <span class="kw">class</span> Code <span class="op">{}</span>;</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>While it is unlikely the user intends for the standard attributes to
be targeted by
<code class="sourceCode cpp"><span class="kw">using</span> CC</code>,
current grammar says that the prefix applies to attributes as they are
found in the <em>subsequent</em> list. To remediate this we can either
enforce that
<code class="sourceCode cpp"><em>splice-name-qualifier</em></code>
precedes
<code class="sourceCode cpp"><em>attribute-using-prefix</em></code> or
have those constructs be mutually exclusive as they occur in <code class="sourceCode cpp"><span class="op">[[</span><span class="at"> </span><span class="op">]]</span></code>.<br />
To reduce the need to memorize unintuitive rules, we favor the later of
those options, as following:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> unscopedAttribute <span class="op">=</span> <span class="op">^[[</span><span class="at">nodiscard</span><span class="op">]]</span>;</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at"> </span><span class="op">[:</span><span class="at"> unscopedAttribute </span><span class="op">:]</span><span class="at"> </span><span class="op">]][[</span><span class="at"> </span><span class="kw">using</span><span class="at"> CC</span><span class="op">:</span><span class="at"> debug </span><span class="op">]]</span> <span class="kw">enum</span> <span class="kw">class</span> Code <span class="op">{}</span>;</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<h2 data-number="3.4" id="metafunctions"><span class="header-section-number">3.4</span> Metafunctions<a href="#metafunctions" class="self-link"></a></h2>
<p>We propose to add two metafunctions to what has already been
discussed in <span class="citation" data-cites="P2996R5">[<a href="https://wg21.link/p2996r5" role="doc-biblioref">P2996R5</a>]</span>. In addition, we will add
support for attributes in the other metafunctions, when it makes
sense.</p>
<h3 data-number="3.4.1" id="attributes_of"><span class="header-section-number">3.4.1</span> attributes_of<a href="#attributes_of" class="self-link"></a></h3>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">namespace</span> std<span class="op">::</span>meta <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>        <span class="kw">consteval</span> <span class="kw">auto</span> attributes_of<span class="op">(</span>info entity<span class="op">)</span> <span class="op">-&gt;</span> vector<span class="op">&lt;</span>info<span class="op">&gt;</span>;</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>Applying this to a reflection
<code class="sourceCode cpp">entity</code> will yield a sequence of
<code class="sourceCode cpp">std<span class="op">::</span>meta<span class="op">::</span>info</code>
representing each individual attribute appertaining to
<code class="sourceCode cpp">entity</code>.</p>
<h3 data-number="3.4.2" id="is_attribute"><span class="header-section-number">3.4.2</span> is_attribute<a href="#is_attribute" class="self-link"></a></h3>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">namespace</span> std<span class="op">::</span>meta <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>        <span class="kw">consteval</span> <span class="kw">auto</span> is_attribute<span class="op">(</span>info entity<span class="op">)</span> <span class="op">-&gt;</span> <span class="dt">bool</span>;</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>This would return true if the
<code class="sourceCode cpp">entity</code> reflection designates a <code class="sourceCode cpp"><span class="op">[</span> <span class="op">[</span> <em>attribute</em> <span class="op">]</span> <span class="op">]</span></code>;
otherwise, it would return false.</p>
<h3 data-number="3.4.3" id="identifier_of-display_identifier_of"><span class="header-section-number">3.4.3</span> identifier_of,
display_identifier_of<a href="#identifier_of-display_identifier_of" class="self-link"></a></h3>
<p>Given a reflection <code class="sourceCode cpp">r</code> designating
a standard attribute, <code class="sourceCode cpp">identifier_of<span class="op">(</span>r<span class="op">)</span></code>
(resp. <code class="sourceCode cpp">u8identifier_of<span class="op">(</span>r<span class="op">)</span></code>)
is encouraged to return a
<code class="sourceCode cpp">string_view</code> (resp.
<code class="sourceCode cpp">u8string_view</code>) corresponding to the
<code class="sourceCode cpp">attribute<span class="op">-</span>token</code>.</p>
<p>A toy example follows</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span>,<span class="at"> deprecated</span><span class="op">(</span><span class="st">&quot;Do not use me&quot;</span><span class="op">)]]</span> <span class="dt">int</span> func<span class="op">()</span>;</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">void</span> print_attributes<span class="op">(</span>std<span class="op">::</span>meta I<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>      <span class="kw">template</span> <span class="cf">for</span> <span class="op">(</span><span class="dt">bool</span> first<span class="op">{</span><span class="kw">true</span><span class="op">}</span>; <span class="kw">constexpr</span> <span class="kw">auto</span> e <span class="op">:</span> std<span class="op">::</span>meta<span class="op">::</span>attributes_of<span class="op">(</span>I<span class="op">))</span> <span class="op">{</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="op">(</span>std<span class="op">::</span>exchange<span class="op">(</span>first, <span class="kw">false</span><span class="op">)</span> <span class="op">?</span> <span class="st">&quot;&quot;</span> <span class="op">:</span> <span class="st">&quot;, &quot;</span><span class="op">)</span> </span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>                  <span class="op">&lt;&lt;</span> std<span class="op">::</span>meta<span class="op">::</span>identifier_of<span class="op">(</span>e<span class="op">)</span> <span class="op">&lt;&lt;</span> std<span class="op">::</span>endl;</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>    print_attributes<span class="op">(^</span>func<span class="op">)</span>; <span class="co">// Prints &quot;nodiscard, deprecated&quot;</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>Given a reflection <code class="sourceCode cpp">r</code> that
designates an individual attribute, <code class="sourceCode cpp">display_identifier_of<span class="op">(</span>r<span class="op">)</span></code>
(resp. <code class="sourceCode cpp">u8display_identifier_of<span class="op">(</span>r<span class="op">)</span></code>)
returns an unspecified non-empty
<code class="sourceCode cpp">string_view</code> (resp.
<code class="sourceCode cpp">u8string_view</code>). Implementations are
encouraged to produce text that is helpful in identifying the reflected
attribute for display purpose. In the preceding example we could imagine
printing <code class="sourceCode cpp"><span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span></code>
instead of <code class="sourceCode cpp">discard</code> as it is more
suitable for display purpose to the user.</p>
<h3 data-number="3.4.4" id="data_member_spec-define_class"><span class="header-section-number">3.4.4</span> data_member_spec,
define_class<a href="#data_member_spec-define_class" class="self-link"></a></h3>
<p>As it stands now, <code class="sourceCode cpp">define_class</code>
allows piecewise building of a class via
<code class="sourceCode cpp">data_member_spec</code>. However, to
support attributes pertaining to those data members, we’ll need to
augment <code class="sourceCode cpp">data_member_options_t</code> to
encode attributes we may want to attach to a data member.<br />
</p>
<p>The structure will change thusly:</p>
<div>
<div class="sourceCode" id="cb11"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    namespace std::meta {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>      struct data_member_options_t {</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>        struct name_type {</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>          template &lt;typename T&gt; requires</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>            constructible_from&lt;u8string, T&gt;</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>            consteval name_type(T &amp;&amp;);</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>          template &lt;typename T&gt; requires</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>            constructible_from&lt;string, T&gt;</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>            consteval name_type(T &amp;&amp;);</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>        };</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>        optional&lt;name_type&gt; name;</span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>        bool is_static = false;</span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>        optional&lt;int&gt; alignment;</span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a>        optional&lt;int&gt; width;</span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a><span class="st">-       bool no_unique_address = false;</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a><span class="va">+       vector&lt;info&gt;  attributes;</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a>      };</span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
</div>
<p>From there building a class piecewise proceeds as usual</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> Empty <span class="op">{}</span>;</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> S;</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>    define_class<span class="op">(^</span>S, <span class="op">{</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>      data_member_spec<span class="op">(^</span><span class="dt">int</span>, <span class="op">{.</span>name <span class="op">=</span> <span class="st">&quot;i&quot;</span><span class="op">})</span>,</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>      data_member_spec<span class="op">(^</span>Empty, <span class="op">{.</span>name <span class="op">=</span> <span class="st">&quot;e&quot;</span>, </span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>                                <span class="op">.</span>attributes <span class="op">=</span> <span class="op">{^[[</span><span class="at">no_unique_address</span><span class="op">]]}})</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">})</span>;</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Equivalent to</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">// struct [[nodiscard]] S {</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">//   int i;</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">//   [[no_unique_address]] Empty e;</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a>    <span class="co">// };</span></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<p>Note here that <span class="citation" data-cites="P2996R5">[<a href="https://wg21.link/p2996r5" role="doc-biblioref">P2996R5</a>]</span> prescribes the use of
<code class="sourceCode cpp">no_unique_address</code> and
<code class="sourceCode cpp">alignment</code> as part of
<code class="sourceCode cpp">data_member_options_t</code>. We think that
this approach scale poorly in that every new attributes introduced into
the standard will lead to discussions on whether or not they deserve to
be included in
<code class="sourceCode cpp">data_member_options_t</code>. There is also
little explanations as to why those were picked among all. Leveraging
reflected attributes through the approach we propose above we think is
more in line with the philosophy of leveraging <code class="sourceCode cpp">std<span class="op">::</span>meta<span class="op">::</span>info</code>
as a black box.</p>
<h3 data-number="3.4.5" id="other-metafunctions"><span class="header-section-number">3.4.5</span> Other metafunctions<a href="#other-metafunctions" class="self-link"></a></h3>
<p>For any reflection where
<code class="sourceCode cpp">is_attribute</code> returns false, other
metafunctions not listed above are not considered constant
expressions</p>
<h2 data-number="3.5" id="queries"><span class="header-section-number">3.5</span> Queries<a href="#queries" class="self-link"></a></h2>
<p>We do not think it is necessary to introduce any additional query or
queries at this point. We would especially not recommend introducing a
dedicated query per attribute (e.g.,
<code class="sourceCode cpp">is_deprecated</code>,
<code class="sourceCode cpp">is_nouniqueaddress</code>, etc.). Having
said that, we feel those should be achievable via concepts, something
akin to:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> deprecatedAttributes <span class="op">=</span> std<span class="op">::</span>meta<span class="op">::</span>attributes_of<span class="op">(^[[</span><span class="at">deprecated</span><span class="op">]])</span>;</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">concept</span> IsDeprecated <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>any_of<span class="op">(</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>      attributes_of<span class="op">(^</span>T<span class="op">)</span>,</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>      <span class="op">[</span>deprecatedAttributes<span class="op">]</span> <span class="op">(</span><span class="kw">auto</span> meta<span class="op">)</span> <span class="op">{</span> meta <span class="op">==</span> deprecatedAttributes<span class="op">[</span><span class="dv">0</span><span class="op">]</span>; <span class="op">}</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">)</span>;</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a></span></code></pre></div>
<h1 data-number="4" id="proposed-wording"><span class="header-section-number">4</span> Proposed wording<a href="#proposed-wording" class="self-link"></a></h1>
<h2 data-number="4.1" id="language"><span class="header-section-number">4.1</span> Language<a href="#language" class="self-link"></a></h2>
<h3 data-number="4.1.1" id="dcl.attr.grammar-attribute-syntax-and-semantics"><span class="header-section-number">4.1.1</span> [dcl.attr.grammar] Attribute
syntax and semantics<a href="#dcl.attr.grammar-attribute-syntax-and-semantics" class="self-link"></a></h3>
<p>Change the grammar to allow splicing attributes from reflection in
<span>9.12.1
<a href="https://wg21.link/dcl.attr.grammar">[dcl.attr.grammar]</a></span>
as follows</p>
<blockquote>
<div class="line-block"><em>attribute-specifier:</em><br />
        [ [ <em>attribute-using-prefix<sub>opt</sub></em>
<em>attribute-list</em> ] ]</div>
<div class="add" style="color: #006e28">

<div class="line-block">        [ [ <em>splice-name-qualifier</em> ]
]</div>

</div>
</blockquote>
<p>Following this subsection modify the paragraph 7</p>
<div class="diff">
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
Two consecutive left square bracket tokens shall appear only when
introducing an
<code class="sourceCode cpp"><em>attribute-specifier</em></code> <span class="rm" style="color: #bf0303"><del>or</del></span><span class="add" style="color: #006e28"><ins>,</ins></span> within the
<code class="sourceCode cpp"><em>balanced-token-seq</em></code> of an
<code class="sourceCode cpp"><em>attribute-argument-clause</em></code>
<span class="add" style="color: #006e28"><ins>or as part of a
<span><code class="sourceCode default"><em>reflect-expression</em></code></span></ins></span>.</p>
</div>
<p>Add the following paragraph</p>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
If an
<code class="sourceCode default"><em>attribute-specifier</em></code>
contains a
<code class="sourceCode default">splice-name-qualifier</code>, every
standard attribute contained in that reflection are applied to the
entity to which that
<code class="sourceCode default"><em>attribute-specifier</em></code> is
attached. The form of this expansion shall be treated as an
<code class="sourceCode default">attribute-list</code>. If
<code class="sourceCode default"><em>splice-name-qualifier</em></code>
describes an entity that bear no attributes, it has no effect.</p>
</div>
<h3 data-number="4.1.2" id="expr.unary.general-general"><span class="header-section-number">4.1.2</span> [expr.unary.general]
General<a href="#expr.unary.general-general" class="self-link"></a></h3>
<p>Augment the
<code class="sourceCode cpp"><span class="op">^</span></code> operator
to allow for reflection on attribute in <span>7.6.2.1
<a href="https://wg21.link/expr.unary.general">[expr.unary.general]</a></span>
as follows</p>
<blockquote>
<div class="line-block"><em>reflect-expression:</em><br />
        <code class="sourceCode cpp"><span class="op">^</span></code>
<code class="sourceCode cpp"><span class="op">::</span></code><br />
        <code class="sourceCode cpp"><span class="op">^</span></code>
<em>namespace-name</em><br />
        <code class="sourceCode cpp"><span class="op">^</span></code>
<em>nested-name-specifier<sub>opt</sub></em>
<em>template-name</em><br />
        <code class="sourceCode cpp"><span class="op">^</span></code>
<em>nested-name-specifier<sub>opt</sub></em> <em>concept-name</em><br />
        <code class="sourceCode cpp"><span class="op">^</span></code>
<em>type-id</em><br />
        <code class="sourceCode cpp"><span class="op">^</span></code>
<em>id-expression</em></div>
<div class="add" style="color: #006e28">

<div class="line-block">        <code class="sourceCode default">^ [ [</code>
<em>attribute</em> <code class="sourceCode default">] ]</code></div>

</div>
</blockquote>
<p>Note here that we are breaking the attributes appertaining rules for
anything but <code class="sourceCode cpp"><span class="op">[[</span><span class="at">assume</span><span class="op">]]</span></code>,
(e.g) in the form <code class="sourceCode cpp"><span class="op">^[[</span><span class="at">nodiscard</span><span class="op">]]</span>;</code>
the <code class="sourceCode cpp">nodiscard</code> attribute is now
appertaining to a null statement which is not allowed.</p>
<p>This is still an open discussion but a suggestion is as following
where we individually address <span>9.12.4
<a href="https://wg21.link/dcl.attr.depend">[dcl.attr.depend]</a></span>.<br />
Edit paragraph 1</p>
<blockquote>
<div class="line-block"><span class="marginalizedparent"><a class="marginalized">1</a></span> The
<em>attribute-token</em>
<code class="sourceCode cpp">carries_dependency</code> specifies
dependency propagation into and out of functions. No <code class="sourceCode cpp">attribute<span class="op">-</span>argument<span class="op">-</span>clause</code>
shall be present. <span class="add" style="color: #006e28"><ins>As part
of a
<span><code class="sourceCode default">reflect-expression</code></span>,
the attribute must apply to the
<span><code class="sourceCode default">null statement</code></span>.
Outside of a
<span><code class="sourceCode default">reflect-expression</code></span>
the</ins></span> <span class="rm" style="color: #bf0303"><del>The</del></span> attribute may be applied to
a parameter of a function or lambda, in which case it specifies that the
initialization of the parameter carries a dependency to
([intro.multithread]) each lvalue-to-rvalue conversion ([conv.lval]) of
that object. The attribute may also be applied to a function or a lambda
call operator, in which case it specifies that the return value, if any,
carries a dependency to the evaluation of the function call
expression.</div>
</blockquote>
<h3 data-number="4.1.3" id="expr.reflect-the-reflection-operator"><span class="header-section-number">4.1.3</span> [expr.reflect] The reflection
operator<a href="#expr.reflect-the-reflection-operator" class="self-link"></a></h3>
<p>Modify the subsection [expr.reflect] to describe reflection applying
to attributes, adding the following paragraph</p>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">11</a></span> When
applied to a <code class="sourceCode default">[ [</code>
<em>attribute</em> <code class="sourceCode default">] ]</code>, the
reflection operator produces a reflection for the indicated
<em>attribute</em>. If <em>attribute</em> is not described in <span>9.12
<a href="https://wg21.link/dcl.attr">[dcl.attr]</a></span>, the behavior
is implementation defined.</p>
</div>
<h3 data-number="4.1.4" id="expr.eq-equality-operators"><span class="header-section-number">4.1.4</span> [expr.eq] Equality
Operators<a href="#expr.eq-equality-operators" class="self-link"></a></h3>
<p>Augment the section in <span>7.6.10
<a href="https://wg21.link/expr.eq">[expr.eq]</a></span> describing
comparisons between <code class="sourceCode cpp">std<span class="op">::</span>meta<span class="op">::</span>info</code>
to specify comparison on reflection over attributes</p>
<div class="add" style="color: #006e28">

<ul>
<li>Otherwise, if one operand represents a standard attribute, then they
compare equal if and only if the other operand represents a standard
attribute and both those attributes have identical
<code class="sourceCode default">attribute-token</code>. If any or both
of those operands have a
<code class="sourceCode default">attribute-argument-clause</code>, the
content of that clause is ignored.</li>
</ul>

</div>
<h2 data-number="4.2" id="library"><span class="header-section-number">4.2</span> Library<a href="#library" class="self-link"></a></h2>
<h3 data-number="4.2.1" id="meta.synop-header-meta-synopsis"><span class="header-section-number">4.2.1</span> [meta.synop] Header
<code class="sourceCode cpp"><span class="op">&lt;</span></code>meta<code class="sourceCode cpp"><span class="op">&gt;</span></code>
synopsis<a href="#meta.synop-header-meta-synopsis" class="self-link"></a></h3>
<p>Add to the [meta.reflection.queries] section from the synopsis the
two metafunctions as follows:</p>
<div class="add" style="color: #006e28">

<blockquote>
<div class="line-block"><code class="sourceCode default">consteval bool is_attribute(info r);</code><br />
<code class="sourceCode default">consteval vector&lt;info&gt; attributes_of(info r);</code></div>
</blockquote>

</div>
<h3 data-number="4.2.2" id="meta.reflection.queries-reflection-queries"><span class="header-section-number">4.2.2</span> [meta.reflection.queries],
Reflection queries<a href="#meta.reflection.queries-reflection-queries" class="self-link"></a></h3>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">42</a></span> <code class="sourceCode default">consteval bool is_attribute(info r);</code></p>
<p>Returns: <code class="sourceCode default">true</code> if r designates
a standard attribute. Otherwise,
<code class="sourceCode default">false</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">43</a></span> <code class="sourceCode default">consteval vector&lt;info&gt; attributes_of(info r);</code></p>
<p>Returns: A vector containing a reflection for each of the attributes
that appertain to the entity r</p>
</div>
<h1 data-number="5" id="future-direction"><span class="header-section-number">5</span> Future direction<a href="#future-direction" class="self-link"></a></h1>
<h2 data-number="5.1" id="scoped-attributes"><span class="header-section-number">5.1</span> Scoped attributes<a href="#scoped-attributes" class="self-link"></a></h2>
<p>The current proposal limits itself to attributes whose semantics are
described by the standard, our argument was mostly based on leaving room
to implementation to do what they want with the other kind of attrbutes.
Also if the conversation around attributes automatically get redirected
into a ‘ignorability’ side conversation, this is even more the case when
it comes to non standard attributes… As we see that there is appetite
for tagging various entities and apply dedicated logic based off those
tags, this is something that we think we can accomodate by extending
this proposal.<br />
</p>
<p>An example of extension would be to introduce</p>
<blockquote>
<div class="line-block"><code class="sourceCode cpp"><span class="kw">consteval</span> vector<span class="op">&lt;</span>info<span class="op">&gt;</span> attributes_of<span class="op">(</span>info r, std<span class="op">::</span>string_view ns<span class="op">)</span>;</code></div>
</blockquote>
<p>Where we intend to filter the attributes returned from the
<code class="sourceCode cpp">r</code> reflection, on a particular <code class="sourceCode cpp">attribute<span class="op">-</span><span class="kw">namespace</span></code>.
So in the following example</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span>,<span class="at"> tag</span><span class="op">:</span><span class="at">Validation</span><span class="op">]]</span> <span class="kw">enum</span> MyEnum <span class="op">{</span> Success, Failure <span class="op">}</span>;</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> <span class="kw">auto</span> tags <span class="op">=</span> std<span class="op">::</span>meta<span class="op">::</span>attributes_of<span class="op">(^</span>MyEnum, <span class="st">&quot;tag&quot;</span><span class="op">)</span>; <span class="co">// Contains &quot;tag:Validation&quot;</span></span></code></pre></div>
<p>Arguably this would reopen a heated discussion on how much
implementations are allowed to ignore scoped attributes, and at what
stage of parsing this is allowed. Based on this, and to avoid
distraction we are not actively exploring this venue.</p>
<h2 data-number="5.2" id="implementation"><span class="header-section-number">5.2</span> Implementation<a href="#implementation" class="self-link"></a></h2>
<p>This proposal is being worked on in a fork from the Clang P2996
branch to be advertised at a later time. However and since more
ambitious reflection related proposals have been implemented already, we
do not foresee dramatic complexity.</p>
<h1 data-number="6" id="conclusion"><span class="header-section-number">6</span> Conclusion<a href="#conclusion" class="self-link"></a></h1>
<p>Originally the idea of introducing a <code class="sourceCode cpp">declattr<span class="op">(</span>Expression<span class="op">)</span></code>
keyword seemed the most straightforward approach to tackling this
problem. However based on feedback, the concern of introspecting on
expression attributes was a topic that belongs with the Reflection SG.
The current proposal shifted away from the original
<code class="sourceCode cpp">declattr</code> idea to align better with
the reflection toolbox. Note also that, as we advocate here for <code class="sourceCode cpp"><span class="op">[[</span><span class="at"> </span><span class="op">[:</span><span class="at"> r </span><span class="op">:]</span><span class="at"> </span><span class="op">]]</span></code>
to be supported, we recover the ease of use that we first envisioned
<code class="sourceCode cpp">declattr</code> to have.</p>
<h1 data-number="7" id="bibliography"><span class="header-section-number">7</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="1" role="doc-bibliography">
<div id="ref-CWG2538" class="csl-entry" role="doc-biblioentry">
[CWG2538] Jens Maurer. 2021-12-02. Can standard attributes be
syntactically ignored? <a href="https://wg21.link/cwg2538"><div class="csl-block">https://wg21.link/cwg2538</div></a>
</div>
<div id="ref-P1887R1" class="csl-entry" role="doc-biblioentry">
[P1887R1] Corentin Jabot. 2020-01-13. Reflection on attributes. <a href="https://wg21.link/p1887r1"><div class="csl-block">https://wg21.link/p1887r1</div></a>
</div>
<div id="ref-P2237R0" class="csl-entry" role="doc-biblioentry">
[P2237R0] Andrew Sutton. 2020-10-15. Metaprogramming. <a href="https://wg21.link/p2237r0"><div class="csl-block">https://wg21.link/p2237r0</div></a>
</div>
<div id="ref-P2552R3" class="csl-entry" role="doc-biblioentry">
[P2552R3] Timur Doumler. 2023-06-14. On the ignorability of standard
attributes. <a href="https://wg21.link/p2552r3"><div class="csl-block">https://wg21.link/p2552r3</div></a>
</div>
<div id="ref-P2565R0" class="csl-entry" role="doc-biblioentry">
[P2565R0] Bret Brown. 2022-03-16. Supporting User-Defined Attributes. <a href="https://wg21.link/p2565r0"><div class="csl-block">https://wg21.link/p2565r0</div></a>
</div>
<div id="ref-P2996R5" class="csl-entry" role="doc-biblioentry">
[P2996R5] Barry Revzin, Wyatt Childers, Peter Dimov, Andrew Sutton,
Faisal Vali, Daveed Vandevoorde, Dan Katz. 2024-08-14. Reflection for
C++26. <a href="https://wg21.link/p2996r5"><div class="csl-block">https://wg21.link/p2996r5</div></a>
</div>
<div id="ref-P3254R0" class="csl-entry" role="doc-biblioentry">
[P3254R0] Brian Bi. 2024-05-22. Reserve identifiers preceded by @ for
non-ignorable annotation tokens. <a href="https://wg21.link/p3254r0"><div class="csl-block">https://wg21.link/p3254r0</div></a>
</div>
<div id="ref-P3385R0" class="csl-entry" role="doc-biblioentry">
[P3385R0] Aurelien Cassagnes, Aurelien Cassagnes, Roman Khoroshikh,
Anders Johansson. 2024-09-16. Attributes reflection. <a href="https://wg21.link/p3385r0"><div class="csl-block">https://wg21.link/p3385r0</div></a>
</div>
</div>
<section class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Discussion in
https://eel.is/c++draft/dcl.attr#depend-2, imply that translation units
need to carry their attributes unchanged to observe that rule.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</div>
</div>
</body>
</html>
