<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 254: Exception types in clause 19 are constructed from std::string</title>
<meta property="og:title" content="Issue 254: Exception types in clause 19 are constructed from std::string">
<meta property="og:description" content="C++ library issue. Status: CD1">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue254.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#CD1">CD1</a> status.</em></p>
<h3 id="254"><a href="lwg-defects.html#254">254</a>. Exception types in clause 19 are constructed from <code>std::string</code></h3>
<p><b>Section:</b> 19.2 <a href="https://wg21.link/std.exceptions">[std.exceptions]</a>, 31.5.2.2.1 <a href="https://wg21.link/ios.failure">[ios.failure]</a> <b>Status:</b> <a href="lwg-active.html#CD1">CD1</a>
 <b>Submitter:</b> Dave Abrahams <b>Opened:</b> 2000-08-01 <b>Last modified:</b> 2023-02-07</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View all other</b> <a href="lwg-index.html#std.exceptions">issues</a> in [std.exceptions].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#CD1">CD1</a> status.</p>
<p><b>Discussion:</b></p>
<p>
Many of the standard exception types which implementations are
required to throw are constructed with a const std::string&amp;
parameter. For example:
</p>

<pre>
     19.1.5  Class out_of_range                          [lib.out.of.range]
     namespace std {
       class out_of_range : public logic_error {
       public:
         explicit out_of_range(const string&amp; what_arg);
       };
     }

   1 The class out_of_range defines the type of objects  thrown  as  excep-
     tions to report an argument value not in its expected range.

     out_of_range(const string&amp; what_arg);

     Effects:
       Constructs an object of class out_of_range.
     Postcondition:
       strcmp(what(), what_arg.c_str()) == 0.
</pre>

<p>
There are at least two problems with this:
</p>
<ol>
<li>A program which is low on memory may end up throwing
std::bad_alloc instead of out_of_range because memory runs out while
constructing the exception object.</li>
<li>An obvious implementation which stores a std::string data member
may end up invoking terminate() during exception unwinding because the
exception object allocates memory (or rather fails to) as it is being
copied.</li>
</ol>

<p>
There may be no cure for (1) other than changing the interface to
out_of_range, though one could reasonably argue that (1) is not a
defect. Personally I don't care that much if out-of-memory is reported
when I only have 20 bytes left, in the case when out_of_range would
have been reported. People who use exception-specifications might care
a lot, though.
</p>

<p>
There is a cure for (2), but it isn't completely obvious. I think a
note for implementors should be made in the standard. Avoiding
possible termination in this case shouldn't be left up to chance.  The
cure is to use a reference-counted &quot;string&quot; implementation
in the exception object. I am not necessarily referring to a
std::string here; any simple reference-counting scheme for a NTBS
would do.
</p>

<p><b>Further discussion, in email:</b></p>

<p>
...I'm not so concerned about (1). After all, a library implementation
can add const char* constructors as an extension, and users don't
<i>need</i> to avail themselves of the standard exceptions, though this is
a lame position to be forced into.  FWIW, std::exception and
std::bad_alloc don't require a temporary basic_string.
</p>

<p>
...I don't think the fixed-size buffer is a solution to the problem,
strictly speaking, because you can't satisfy the postcondition
<br/>
    <code>&nbsp;&nbsp;strcmp(what(), what_arg.c_str()) == 0</code>
<br/>
For all values of what_arg (i.e. very long values). That means that
the only truly conforming solution requires a dynamic allocation.
</p>

<p><b>Further discussion, from Redmond:</b></p>

<p>The most important progress we made at the Redmond meeting was
realizing that there are two separable issues here: the const
string&amp; constructor, and the copy constructor.  If a user writes
something like <code>throw std::out_of_range("foo")</code>, the const
string&amp; constructor is invoked before anything gets thrown.  The
copy constructor is potentially invoked during stack unwinding.</p>

<p>The copy constructor is a more serious problem, becuase failure
during stack unwinding invokes <code>terminate</code>.  The copy
constructor must be nothrow. <i>Cura&ccedil;ao: Howard thinks this
requirement may already be present.</i></p>

<p>The fundamental problem is that it's difficult to get the nothrow
requirement to work well with the requirement that the exception
objects store a string of unbounded size, particularly if you also try
to make the const string&amp; constructor nothrow.  Options discussed
include:</p>

<ul>
<li>Limit the size of a string that exception objects are required to
throw: change the postconditions of 19.2.4 <a href="https://wg21.link/domain.error">[domain.error]</a> paragraph 3
and 19.2.8 <a href="https://wg21.link/runtime.error">[runtime.error]</a> paragraph 3 to something like this:
"strncmp(what(), what_arg._str(), N) == 0, where N is an
implementation defined constant no smaller than 256".</li>
<li>Allow the const string&amp; constructor to throw, but not the
copy constructor.  It's the implementor's responsibility to get it
right.  (An implementor might use a simple refcount class.)</li>
<li>Compromise between the two: an implementation is not allowed to
throw if the string's length is less than some N, but, if it doesn't
throw, the string must compare equal to the argument.</li>
<li>Add a new constructor that takes a const char*</li>
</ul>

<p>(Not all of these options are mutually exclusive.)</p>



<p id="res-254"><b>Proposed resolution:</b></p>

<p>
Change 19.2.3 <a href="https://wg21.link/logic.error">[logic.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class logic_error : public exception {
  public:
    explicit logic_error(const string&amp; <i>what_arg</i>);
    <ins>explicit logic_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>logic_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>logic_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.4 <a href="https://wg21.link/domain.error">[domain.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class domain_error : public logic_error {
  public:
    explicit domain_error(const string&amp; <i>what_arg</i>);
    <ins>explicit domain_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>domain_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>domain_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>

</blockquote>
</blockquote>

<p>
Change 19.2.5 <a href="https://wg21.link/invalid.argument">[invalid.argument]</a>
</p>

<blockquote>
<pre>
namespace std {
  class invalid_argument : public logic_error {
  public:
    explicit invalid_argument(const string&amp; <i>what_arg</i>);
    <ins>explicit invalid_argument(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>invalid_argument(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>invalid_argument</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.6 <a href="https://wg21.link/length.error">[length.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class length_error : public logic_error {
  public:
    explicit length_error(const string&amp; <i>what_arg</i>);
    <ins>explicit length_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>length_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>length_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.7 <a href="https://wg21.link/out.of.range">[out.of.range]</a>
</p>

<blockquote>
<pre>
namespace std {
  class out_of_range : public logic_error {
  public:
    explicit out_of_range(const string&amp; <i>what_arg</i>);
    <ins>explicit out_of_range(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>out_of_range(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>out_of_range</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.8 <a href="https://wg21.link/runtime.error">[runtime.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class runtime_error : public exception {
  public:
    explicit runtime_error(const string&amp; <i>what_arg</i>);
    <ins>explicit runtime_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>runtime_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>runtime_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.9 <a href="https://wg21.link/range.error">[range.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class range_error : public runtime_error {
  public:
    explicit range_error(const string&amp; <i>what_arg</i>);
    <ins>explicit range_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>range_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>range_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.10 <a href="https://wg21.link/overflow.error">[overflow.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class overflow_error : public runtime_error {
  public:
    explicit overflow_error(const string&amp; <i>what_arg</i>);
    <ins>explicit overflow_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>overflow_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>overflow_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change 19.2.11 <a href="https://wg21.link/underflow.error">[underflow.error]</a>
</p>

<blockquote>
<pre>
namespace std {
  class underflow_error : public runtime_error {
  public:
    explicit underflow_error(const string&amp; <i>what_arg</i>);
    <ins>explicit underflow_error(const char* <i>what_arg</i>);</ins>
  };
}
</pre>
<p>...</p>
<p>
<ins><code>underflow_error(const char* <i>what_arg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>underflow_error</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>what_arg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>

<p>
Change  [ios::failure]
</p>

<blockquote>
<pre>
namespace std {
  class ios_base::failure : public exception {
  public:
    explicit failure(const string&amp; <i>msg</i>);
    <ins>explicit failure(const char* <i>msg</i>);</ins>
    virtual const char* what() const throw();
};
}
</pre>
<p>...</p>
<p>
<ins><code>failure(const char* <i>msg</i>);</code></ins>
</p>
<blockquote>
<p><ins>
-4- <i>Effects:</i> Constructs an object of class <code>failure</code>.
</ins></p>
<p><ins>
-5- <i>Postcondition:</i> <code>strcmp(what(), <i>msg</i>) == 0</code>.
</ins></p>
</blockquote>

</blockquote>



<p><b>Rationale:</b></p>

<p>Throwing a bad_alloc while trying to construct a message for another
exception-derived class is not necessarily a bad thing.  And the
bad_alloc constructor already has a no throw spec on it (18.4.2.1).</p>

<p><b>Future:</b></p>

<p>All involved would like to see const char* constructors added, but
this should probably be done for C++0X as opposed to a DR.</p>

<p>I believe the no throw specs currently decorating these functions
could be improved by some kind of static no throw spec checking
mechanism (in a future C++ language).  As they stand, the copy
constructors might fail via a call to unexpected.  I think what is
intended here is that the copy constructors can't fail.</p>

<p><i>[Pre-Sydney: reopened at the request of Howard Hinnant.
  Post-Redmond: James Kanze noticed that the copy constructors of
  exception-derived classes do not have nothrow clauses.  Those
  classes have no copy constructors declared, meaning the
  compiler-generated implicit copy constructors are used, and those
  compiler-generated constructors might in principle throw anything.]</i></p>


<p><i>[
Batavia:  Merged copy constructor and assignment operator spec into <code>exception</code>
and added <code>ios::failure</code> into the proposed resolution.
]</i></p>


<p><i>[
Oxford:  The proposed resolution simply addresses the issue of constructing
the exception objects with <code>const char*</code> and string literals without
the need to explicit include or construct a <code>std::string</code>.
]</i></p>







</body>
</html>
