<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>P0785R0: Runtime-sized arrays and a C++ wrapper</title>

<style type="text/css">
  ins,.new { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
  .new { text-decoration:none; font-weight:bold; background-color:#D0FFD0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
  strong { font-weight: inherit; color: #2020ff }
  table, td, th { border: 1px solid black; border-collapse:collapse; padding: 5p
x }
</style>
</head>

<body>
ISO/IEC JTC1 SC22 WG21 P0785R0<br/>
Jens Maurer &lt;Jens.Maurer@gmx.net><br/>
Target audience: EWG, LEWG<br/>
2017-09-30<br/>

<h1>P0785R0: Runtime-sized arrays and a C++ wrapper</h1>

<h2>Introduction</h2>

<p>
It is occasionally useful to allocate small data structures on the
stack that have a variable size defined at construction, which would
otherwise only be possible by using heap allocation.
</p>

<p>
Examples:

<ul>
<li>interfacing with the readv/writev POSIX system calls</li>
<li>avoiding heap allocations, since it accesses a global resource
that might be contended in a multi-threaded program</li>
<li>small runtime-sized data structure to apply STL algorithms calls</li>
</ul>

<p>

The C language provides variable-length arrays (VLAs) for that
purpose, with the syntax
</p>
<pre>
  void f(int n)
  {
    int a[n];
  }
</pre>

<p>
Most C++ compilers implement this facility also in C++ mode.
</p>

<p>This paper proposes to standardize a subset of the C-language VLA
facility and a simple C++ class (similar
to <code>std::initializer_list</code>) for the purpose of stack
allocation of runtime-sized data structures.

<p>
For more detailed motivation and introductory explanations, please see
the papers cited in the next section.
</p>


<h2>History</h2>

<p>
Standardizing the basic C VLA syntax (but not all the anciliary
features of C VLAs) was the subject of "N3639 Runtime-sized arrays
with automatic storage duration (revision 5)" (by Jens Maurer), which
was voted into the C++ working paper in Bristol, April 2013.
</p>

<p>
A companion facility with a C++-style library interface called
<code>std::dynarrray</code> was specified in "N3662 C++ Dynamic Arrays
(dynarray)" (by Lawrence Crowl and Matt Austern), which was also voted
into the C++ working paper in Bristol, April 2013.
</p>

<p>
Both facilities were removed from C++14 and instead put into a
separate Technical Specification in Chicago, September 2013.
Authority for doing so came from National Body comment CH 2 on the
C++14 Committee Draft ballot, asking for removal of any feature
degrading the quality of the upcoming standard. The technical grounds
for removal were the usability of <code>std::dynarray</code> for
objects with any kind of storage duration, which made it look like a
construction-sized <code>std::vector</code> with optional compiler
optimizations, instead of a specialized stack allocation facility with
a C++ look and feel that some members of the committee prefer.  The
working draft of the Technical Specification was published as "N3820
Working Draft, Technical Specification - Array Extensions" (editor:
Lawrence Crowl).
</p>

<p>
After the Chicago meeting, there was additional exploration of the
design space in the papers

<ul>
<li>N3810 Alternatives for Array Extensions (by Bjarne Stroustrup)</li>
<li>N3875 Run-time bound array data members (by Daniel Garcia and Xin Li)</li>
</ul>
</p>

<p>
The Issaquah 2014 meeting saw a lengthy discussion of the design
approaches, but little actual progress.
</p>

<p>
The Array TS project was canceled in Jacksonville, March 2016.
</p>


<h2>General remarks</h2>

<p>
The C++ standard is silent on concepts such as "stack" or "heap".
Instead, objects have different kinds of storage duration, depending
on how they came to exist.  Therefore, it would be new grounds to
normatively specify that certain kinds of objects must be allocated on
the stack (and not on the heap).  As a quality-of-implementation
concern, most implementations already do the obvious.  However,
implementations for special target audiences may diverge from that
de-facto consensus.  For example, when stack space is severly limited
for environmental reasons (e.g. in kernel or embedded code), it seems
plausible for a compiler to transparently allocate (and deallocate)
large objects from some other pool of memory and only store a simple
pointer on the stack.  Therefore, the normative specification can only
make sure that stack allocation is actually possible and, at best,
encourage it non-normatively.
</p>

<p>
Construction of an object that is a runtime-sized data structure (in
short, an "object of runtime size") requires a three-step approach:
determine size, allocate storage, invoke the constructor.  These are
exactly the same three steps that are required for the expression
"<code>new T[n]</code>".
</p>

<p>
There are two conflicting opinions on the general design approach for
a C++-style library interface (or building blocks enabling such),
which cannot be reconciled:

<ul>
<li>a best-effort class without usage restrictions that allocates from
the heap unless tailored compiler optimizations turn that into
allocation on the stack (<code>std::dynarray</code>)</li>

<li>a class with compiler-enforced usage restrictions that make it
possible to always allocate from the stack
(<code>std::bs_array</code>)</li>

</ul>

(As explained in the first paragraph, it is beyond the scope of the C++
standard to actually enforce stack allocation, even in the latter
case.)
</p>

<h2>Design discussion</h2>

<p>
This proposal re-proposes "N3639 Runtime-sized arrays with automatic
storage duration (revision 5)", by Jens Maurer, together with a simple
C++ class similar to <code>bs_array</code> proposed in "N3810
Alternatives for Array Extensions", by Bjarne Stroustrup.  Please see
these papers for detailed motivation and discussion.
</p>

<h2>Naming</h2>

The proposed name for the C++ class is <code>stack_array</code>.


<h2>Proposed Wording</h2>

Change in 7.2 [conv.array] paragraph 1:

<blockquote>
An <del>lvalue or rvalue</del> <ins>expression</ins> of type "array of
N T"<ins>, "array of runtime bound of T",</ins> or "array of unknown
bound of T" can be converted to a prvalue of type "pointer to T". The
result is a pointer to the first element of the array.
</blockquote>

Change in 8.1.5.2 [expr.prim.lambda.capture] paragraph 11:

<blockquote>
[...] [ Note: The cast ensures that the transformed expression is a
prvalue. -- end note ] <ins>An array of runtime bound (11.3.4
[dcl.array]) shall not be captured by copy.</ins>
An <em>id-expression</em> within the <em>compound-statement</em> of
a <em>lambda-expression</em> that is an odr-use of a reference
captured by reference refers to the entity to which the captured
reference is bound and not to the captured reference.  [ Note: ... ]
</blockquote>

Change in 8.1.5.2 [expr.prim.lambda.capture] paragraph 12:

<blockquote>
An entity is <em>captured by reference</em> if it is implicitly or
explicitly captured but not captured by copy. It is unspecified
whether additional unnamed non-static data members are declared in the
closure type for entities captured by reference.  If declared, such
non-static data members shall be of literal type. [ Example: ... ]
<ins>[ Note: Capturing by reference an array of runtime bound also
implicitly captures the value of the bound to support the range-based
for statement (9.5.4 [stmt.ranged]).]</ins> A bit-field or a member of
an anonymous union shall not be captured by reference.
</blockquote>

Insert a new paragraph before 8.2.8 [expr.typeid] paragraph 2:

<blockquote>
<ins>The <code>typeid</code> operator shall not be applied to an array
of runtime bound (11.3.4 [dcl.array]).</ins>
<p>
When <code>typeid</code> is applied to a glvalue expression whose type
is a polymorphic class type (13.3), ...
</blockquote>

Change in 8.3.1 [expr.unary.op] paragraph 3:

<blockquote>
The result of the unary &amp; operator is a pointer to its
operand. The operand shall be an lvalue <ins>of type other than "array
of runtime bound" (11.3.4 [dcl.array])</ins> or
a <em>qualified-id</em>. ...
</blockquote>


Change in 8.3.3 [expr.sizeof] paragraph 1:

<blockquote>
... The <code>sizeof</code> operator shall not be applied to an
expression that has function or incomplete type, <ins>to an array of
runtime bound (11.3.4 [dcl.array]),</ins> to the parenthesized name of
such types, or to a glvalue that designates a bit-field. ...
</blockquote>

<p>
<em>Drafting note: 8.3.7 [expr.unary.noexcept] does not need to be
changed, because the declaration of an array of runtime bound cannot
be lexically part of the operand of a noexcept; see also 8.1.5
[expr.prim.lambda] paragraph 2.</em>
</p>

<em>No change to 9.5.4 [stmt.ranged] paragraph 1, supporting
runtime-sized arrays implicitly.</em>

<p>

Insert a new paragraph before 10.1.3 [dcl.typedef] paragraph 3:

<blockquote>
<ins>A <var>typedef-name</var> shall not name an array of runtime
bound (11.3.4 [dcl.array]).</ins>
<p>
In a given non-class scope, a <code>typedef</code> specifier can be used to
redefine the name of any type declared in that scope to refer to the
type to which it already refers. [ Example: ... ]
</blockquote>

Change in 10.1.7.2 [dcl.type.simple] paragraph 5:

<blockquote>
For an expression e, the type denoted by <code>decltype(e)</code> is
defined as follows:
<ul>
  <li><ins>if <code>e</code> has type "array of runtime bound" (11.3.4
      [dcl.array]), the program is ill-formed;</ins></li>
  <li>if e is an unparenthesized <em>id-expression</em> naming a structured
    binding (11.5), ...</li>
</ul>

</blockquote>

Change in 11 [dcl.decl] paragraph 4:

<blockquote>
<pre>
<em>noptr-declarator:
      declarator-id attribute-specifier-seq<sub>opt</sub>
      noptr-declarator parameters-and-qualifiers
      noptr-declarator</em> [ <del><em>constant-expression</em><sub>opt</sub></del> <ins><em>expression</em><sub>opt</sub></ins> ] <em>attribute-specifier-seq</em><sub>opt</sub>
      ( <em>ptr-declarator</em> )
</pre>
</blockquote>

<p>
<em>Drafting note: Section 11.1 [dcl.name] defining the grammar term
<em>type-id</em> is intentionally unchanged.  Thus, constructing an
array of runtime bound in a <em>type-id</em> is ill-formed, because
the grammar continues to require all constant-expressions in array
bounds.</em>
</p>

Change in 11.3.1 [dcl.ptr] paragraph 1:

<blockquote>
... Similarly, the optional <var>attribute-specifier-seq</var> (7.6.1)
appertains to the pointer and not to the object pointed
to.  <ins>There shall be no pointers to arrays of runtime bound.</ins>
</blockquote>

Change in 11.3.2 [dcl.ref] paragraph 5:

<blockquote>
There shall be no references to references, <ins>no references to
arrays of runtime bound</ins>, no arrays of references, and no
pointers to references. ...
</blockquote>

Change in 11.3.4 [dcl.array] paragraph 1:

<blockquote>
In a declaration T D where D has the form
<pre>
           D1 [ <del><var>constant-expression</var><sub>opt</sub></del> <ins><va
r>expression</var><sub>opt</sub></ins> ] <var>attribute-specifier-seq<var><sub>opt</sub>
</pre>

and the type of the identifier in the declaration T D1 is
"<em>derived-declarator-type-list</em> T", then the type of the
identifier of D is an array type; if the type of the identifier of D
contains the <code>auto</code> <var>type-specifier</var>, the program
is ill-formed.  T is called the array element type; this type shall not be a reference
type, the (possibly cv-qualified) type <code>void</code>, a function
type, <ins>an array of unknown or runtime bound,</ins> or an abstract
class type.
<p>
  
<del>If the constant-expression (8.20 [expr.const]) is present, it
shall be a converted constant expression of
type <code>std::size_t</code> and its value shall be greater than
zero. The constant expression specifies the bound of (number of
elements in) the array. If the value of the constant expression is N,
the array has N elements numbered 0 to N-1, and the type of the
identifier of D is "<em>derived-declarator-type-list</em> array of N
  T".</del>
<ins>Except as noted below, if the <var>expression</var> is omitted,
the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of unknown bound of T".
If the <em>expression</em> is present, it is implicitly converted to
<code>std::size_t</code>.  The <em>expression</em> is erroneous
if:</ins>

<ul>
<li><ins>its value before converting to <code>std::size_t</code> or,
in the case of
an expression of class type, before application of the second standard
conversion (13.3.3.1.2 over.ics.user) is less than or equal
to zero;</ins></li>
<li><ins>its value is such that the size of the allocated object would
exceed the implementation-defined limit (Annex B
[implimits]);</ins></li>
<li><ins>the initializer of the object is a
<em>braced-init-list</em> whose number of (top-level)
<em>initializer-clause</em>s exceeds the number of elements to
initialize; or</ins></li>
<li><ins>the object is initialized with a string literal and there
are more initializers than there are array elements.</ins></li>
</ul>

<ins>If the <em>expression</em>, after converting to
<code>std::size_t</code>, is a core constant expression and the
<em>expression</em> is erroneous, the program is ill-formed.</ins>

<ul>
<li><ins>If the <em>expression</em>, after converting to
<code>std::size_t</code>, is a core constant expression whose
value is <code>N</code>, the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of N T".</ins></li>
<li><ins>Otherwise, the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of runtime bound of T"
and the value of the <var>expression</var> designates the number of
elements <code>N</code> in the array.  If the <em>expression</em> is
erroneous, an exception of a type that would match a handler (15.3
except.handle) of type <code>std::bad_array_length</code>
(21.6.2.2a [array.badlength]) is thrown.</ins></li>
</ul>

An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T. <del>Except as noted below, if the constant
expression is omitted, the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of unknown bound of T",
an incomplete object type.</del>

The type "<em>derived-declarator-type-list</em> array of N T" is a
different type from the type "<em>derived-declarator-type-list</em>
array of unknown bound of T", see 3.9 basic.types.  Any type of the
form "<em>cv-qualifier-seq</em> array of N T" is adjusted to "array of
N <em>cv-qualifier-seq</em> T", and similarly for "array of unknown
bound of T" <ins>and "array of runtime bound of
T"</ins>.

The optional <em>attribute-specifier-seq</em> appertains to the
array. [ Example: 
<pre>
  typedef int A[5], AA[2][3];
  typedef const A CA;         // type is "array of 5 const int"
  typedef const AA CAA;       // type is "array of 2 array of 3 const int"

<ins>  void f(unsigned int n) {
    int a[n];     // type of "a" is "array of runtime bound of int"
  }</ins>

</pre>
-- end example ] [ Note: ... ]

</blockquote>

Change in 11.3.4 [dcl.array] paragraph 3:

<blockquote>
When several "array of" specifications are adjacent, a
multidimensional array is created<del>; only the first of the constant
expressions that specify the bounds of the arrays may be
omitted</del>.  In addition to ...

</blockquote>

Add a new paragraph before 11.3.4 [dcl.array] paragraph 4:

<blockquote class="new">
An array of runtime bound shall only be used as the type of a
local object with automatic storage duration.  If the size of the
array exceeds the size of the memory available for objects with
automatic storage duration, the behavior is undefined [ Footnote:
Implementations that detect this case should throw an
exception that would match a handler (18.3 [except.handle]) of type
<code>std::bad_array_length</code> (21.6.3.2a [array.badlength]). ]  It is
unspecified whether a global allocation function (6.7.4
[basic.stc.dynamic]) is invoked to obtain storage for the array.  If it
is invoked, the corresponding global deallocation function is invoked
to release the storage after the lifetime of the array ended.  [
Footnote: Alternatively, an implementation could allocate such an
array on the usual stack or on a side stack. ]

</blockquote>

Change in 11.3.5 [dcl.fct] paragraph 11:

<blockquote>
<ins>If the type of a parameter is an array of runtime bound, the
program is ill-formed.</ins> Functions shall not have a return type of
type array or function, although they may have a return type of ype
pointer or reference to such things. ...
</blockquote>

Change in 11.6.1 [dcl.init.aggr] paragraph 10:

<blockquote>
<ins>For types other than arrays of runtime bound (11.3.4
[dcl.array]), an</ins>
<del>An</del> <var>initializer-list</var> is ill-formed if the number of
<em>initializer-clause</em>s exceeds the number of members or elements
to initialize. [ Example: ... ]
</blockquote>

Change in 11.6.2 [dcl.init.string] paragraph 2:

<blockquote>
<ins>[ Note:</ins> There shall not be more initializers than there are
array elements<ins>; see 11.3.4 [dcl.array]</ins>. [ Example:
<pre>
 char cv[4] = "asdf";                 // error
</pre>
is ill-formed since there is no space for the implied trailing
'\0'. -- end example ]
<ins> -- end note ]</ins>
</blockquote>

Change in 12.2 [class.mem] paragraph 14:

<blockquote>
<del>Non-static</del> <ins>A non-static</ins> (12.4 [class.static]) data
<del>members</del> <ins>member</ins> shall not have incomplete
<del>types.</del> <ins>type or type "array of runtime bound" (11.3.4
[dcl.array]). [ Note:
</ins> In particular, a class C shall not contain a non-static member
of class C, but it can contain a pointer or reference to an object of
class C. <ins>]</ins>

</blockquote>

Change in 17.1 [temp.param] paragraph 7:
<blockquote>
A non-type <em>template-parameter</em> shall not be declared to have
floating point, class, <ins>array of runtime bound (11.3.4
[dcl.array])</ins>, or void type.  [ Example: ... ]
</blockquote>

<p>
<em>Drafting note: It is not necessary to explicitly prevent template
argument deduction for an array of runtime bound, because 17.9.2
[temp.deduct] paragraph 8 says "If a substitution results in an
invalid type or expression, type deduction fails. An invalid type or
expression is one that would be ill-formed if written using the
substituted arguments."  11.3.5 [dcl.fct] paragraph 11 (and other
places) establishing restrictions on forming types are thus
sufficient.</em>
</p>

Change in 18.1 [except.throw] paragraph 1:

<blockquote>
Throwing an exception transfers control to a handler. [ Note: An
exception can be thrown from one of the following contexts:
<em>throw-expressions</em> (8.17 [expr.throw]), allocation functions
(6.7.4.1 [basic.stc.dynamic.allocation]), <code>dynamic_cast</code>
(8.2.7 [expr.dynamic.cast]), <code>typeid</code> (8.2.8
[expr.typeid]), <em>new-expression</em>s (8.3.4
[expr.new]), <ins>array of runtime bound (11.8.3 [dcl.array]),</ins>
and standard library functions (20.4.1.4
[structure.specifications]). -- end note ] An object is passed and the
type of that object determines which handlers can catch it. [ Example:
... ]

</blockquote>

Add a new section immediately before 21.6.3.2 [new.badlength]:

<blockquote class="new">
<b>21.6.3.2a Class <code>bad_array_length</code> [array.badlength]</b>
<pre>
  namespace std {
    class bad_array_length : public bad_alloc {
    public:
       bad_array_length() noexcept;
       const char* what() const noexcept override;
    };
  }
</pre>

The class <code>bad_array_length</code> defines the type of objects
thrown as exceptions by the implementation to report an attempt to
allocate an array of runtime bound with a size less than or equal to
zero or greater than an implementation-defined limit (11.3.4
[dcl.array]).
<pre>
  bad_array_length() noexcept;
</pre>

<em>Effects:</em> constructs an object of class
<code>bad_array_length</code>.

<pre>
  const char* what() const noexcept override;
</pre>

<em>Returns:</em> An implementation-defined NTBS.<br/>
<em>Remarks:</em> The message may be a null-terminated multibyte
     string (20.4.2.1.5.2 [multibyte.strings]), suitable for
     conversion and display as a <code>wstring</code> (24.3
     [string.classes], 25.4.1.4 [locale.codecvt]).

</blockquote>

Add a new section immediately after 21.9 [support.initlist]:

<blockquote class="new">
<b>21.10 Stack-based arrays [support.stack_array]</b>
<p>
The header <code>&lt;stack_array></code> defines a class template for
an array type which can only be used for variables of automatic
storage duration and whose size is defined at construction. A <code>stack_array</code> is a contiguous container (26.2.1 [container.requirements.general]). All
functions specified in this subclause are signal-safe (21.10.4
[support.signal]).
</p>
<p>
A <code>stack_array</code> satisfies all of the requirements of a
container (26.2 [container.requirements]), except that default
construction and <code>swap</code> are not supported.
A <code>stack_array</code> satisfies some of the requirements of a
sequence container (26.2.3 [sequence.reqmts]). Descriptions are
provided here only for operations on <code>stack_array</code> that are
not described in one of these tables and for operations where there is
additional semantic information.

</p>

<p>
<b>21.10.1 Header <code>&lt;stack_array></code> synopsis [stack_array.syn]</b>

<pre>
template&lt;class T>
class stack_array {
  using value_type      = T;
  using pointer         = T*;
  using const_pointer   = const T*;
  using reference       = T&;
  using const_reference = const T&;
  using size_type       = size_t;
  using difference_type = ptrdiff_t;
  using iterator        = <em>implementation-defined</em> ; 
  using const_iterator  = <em>implementation-defined</em> ; 

  explicit stack_array(size_type n);
  stack_array(size_type n, const T& value); 
  stack_array(const stack_array&);
  stack_array& operator=(const stack_array&);

  // iterators:
  constexpr iterator               begin() noexcept;
  constexpr const_iterator         begin() const noexcept;
  constexpr iterator               end() noexcept;
  constexpr const_iterator         end() const noexcept;
  constexpr const_iterator         cbegin() const noexcept;
  constexpr const_iterator         cend() const noexcept;

  // capacity:
  constexpr bool      empty() const noexcept;
  constexpr size_type size() const noexcept;
  constexpr size_type max_size() const noexcept;

  // element access:
  constexpr  reference       operator[](size_type n);
  constexpr  const_reference operator[](size_type n) const;
  constexpr  reference       at(size_type n);
  constexpr  const_reference at(size_type n) const;
  constexpr  reference       front();
  constexpr  const_reference front() const;
  constexpr  reference       back();
  constexpr  const_reference back() const;

  constexpr T *       data() noexcept;
  constexpr const T * data() const noexcept;
};
</pre>
<p>
<b>21.10.2 Constructors [stack_array.cons]</b>
<p>
Declaring or creating an object of type <code>stack_array</code> is
ill-formed unless that object is a variable with automatic storage
duration.  The destructor of a <code>stack_array</code> shall not be
invoked explicitly (8.2.2 [expr.call]), but only implicitly (9.6
[stmt.jump]).

<pre>
  stack_array(size_type n);
</pre>
<em>Effects:</em> Constructs a <code>stack_array</code> with n
default-constructed elements.<br/>
<em>Requires:</em> n is greater than 0. T shall be DefaultConstructible.<br/>
<em>Complexity:</em> Linear in n.

<pre>
  stack_array(size_type n, const T& value);
</pre>
<em>Effects:</em> Constructs a <code>stack_array</code> with n copies
of <code>value</code>.<br/>
<em>Requires:</em> n is greater than 0. T shall be CopyConstructible.<br/>
<em>Complexity:</em> Linear in n.

<p>
  <b>21.10.3 Member functions [stack_array.mem]</b>
<pre>
  constexpr size_type size() const noexcept;
</pre>
<em>Returns:</em> The number of elements <code>n</code> used for construction.</em>

<pre>
  constexpr T* data() noexcept;
  constexpr const T* data() const noexcept;
</pre>
<em>Returns:</em> A pointer such that <code>data() ==
addressof(front())</code>, and [<code>data(), data() + size()</code>)
is a valid range.

</blockquote>

</body>
</html>
