Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions doc/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -556,12 +556,13 @@ mode there.
number of arguments have to be refused, and `gaol::textToInterval` has to
read the names of GAOL, and with two strings to take the left bound of the
first and the right bound of the second. What the exceptions of GAOL say
has to be their explanation, or `gaol_exception` where there is none: the
`what()` of a `gaol_exception`, an `input_format_error`, an
has to be their explanation, or the name of their class where there is none:
the `what()` of a `gaol_exception`, an `input_format_error`, an
`unavailable_feature_error` and an `invalid_action_error` built with a known
explanation, and of what the reader, `operator>>` and `nb_fp_numbers()`
throw, read through a `std::exception`; and `operator<<` has to write the
explanation once. `what()` was `std::exception`, which a handler of
explanation, without one, with a null pointer (no explanation) and with an
explanation that starts with a NUL, and of what the reader, `operator>>` and
`nb_fp_numbers()` throw, read through a `std::exception`; and `operator<<` has
to write the explanation once. `what()` was `std::exception`, which a handler of
`std::exception` printed and an exception that nothing catches ended the
program with, and `operator<<` wrote it next to the explanation (GAOL v5).
The reading of
Expand Down
36 changes: 29 additions & 7 deletions gaol/gaol_exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ namespace gaol_core {
which_line_=l;
}

gaol_exception::gaol_exception(const char* f, unsigned l, const char* e)
{
which_file_=f;
which_line_=l;
// A null pointer is no explanation: the derived classes passed it on to
// the constructor of std::string, which is undefined behavior (GAOL v5)
if (e != nullptr) {
explanation_ = e;
}
}

gaol_exception::gaol_exception(const char* f, unsigned l, const std::string& e)
{
which_file_=f;
Expand All @@ -67,24 +78,35 @@ namespace gaol_core {
return explanation_;
}

const char*
gaol_exception::class_name() const noexcept
{
return "gaol_exception";
}

/*
The explanation, or a text of its own where there is none, so that what a
handler of std::exception prints, or the terminate handler of an exception
nothing catches, is never empty (GAOL v5). GAOL 4 left the what() of
std::exception, "std::exception", whatever went wrong.
The explanation, or the name of the class where there is none, so that
what a handler of std::exception prints, or the terminate handler of an
exception nothing catches, is never empty (GAOL v5). An explanation whose
C string is empty, a NUL first, is none: what() would be an empty text.
GAOL 4 left the what() of std::exception, "std::exception", whatever went
wrong.
*/
const char*
gaol_exception::what() const noexcept
{
return explanation_.empty() ? "gaol_exception" : explanation_.c_str();
const char* text = explanation_.c_str();
return (*text != '\0') ? text : class_name();
}

std::ostream& operator<<(std::ostream& out, const gaol_exception &e)
{
// what() is the explanation now, written below: not here too (GAOL v5)
out << e.file() << ", line " << e.line() << ": exception thrown";
if (e.explanation().length() != 0) { // Some explanation given?
out << ": " << e.explanation();
// explanation() returns a copy: one, not one per use
const std::string explanation = e.explanation();
if (explanation.length() != 0) { // Some explanation given?
out << ": " << explanation;
}
return out;
}
Expand Down
38 changes: 35 additions & 3 deletions gaol/gaol_exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ namespace gaol_core {
*/
gaol_exception(const char* f, unsigned l);

/*!
\param f Name of the file where the exception is thrown
\param l Line in the file where the exception is thrown
\param e Explanation concerning the throwing; a null pointer is no
explanation (GAOL v5: GAOL 4 built a std::string from it, which is
undefined behavior)
*/
gaol_exception(const char* f, unsigned l, const char* e);

/*!
\param f Name of the file where the exception is thrown
\param l Line in the file where the exception is thrown
Expand Down Expand Up @@ -93,9 +102,10 @@ namespace gaol_core {
went wrong: that is what a catch (const std::exception&) printed, and
what a program ended with when nothing caught the exception.

\return the explanation, as explanation() gives it, or the text
"gaol_exception" if none was given, never an empty text. The text is
valid as long as the exception is.
\return the explanation, as explanation() gives it, or the name of the
class ("gaol_exception", "input_format_error"...) if none was given or
if its text as a C string is empty (a NUL first), never an empty text.
The text is valid as long as the exception is.
*/
const char* what() const noexcept override;

Expand All @@ -116,6 +126,13 @@ namespace gaol_core {
\brief Short explanation concerning the exception thrown.
*/
std::string explanation_;

private:
/*!
\brief The name of the class, which what() gives where there is no
explanation (GAOL v5)
*/
virtual const char* class_name() const noexcept;
};


Expand All @@ -133,6 +150,11 @@ namespace gaol_core {
gaol_exception(f,l,e) {}
input_format_error(const char* f, unsigned l, const std::string& e) :
gaol_exception(f,l,e) {}

private:
const char* class_name() const noexcept override {
return "input_format_error";
}
};


Expand All @@ -148,6 +170,11 @@ namespace gaol_core {
gaol_exception(f,l,e) {}
unavailable_feature_error(const char* f, unsigned l, const std::string& e) :
gaol_exception(f,l,e) {}

private:
const char* class_name() const noexcept override {
return "unavailable_feature_error";
}
};


Expand All @@ -164,6 +191,11 @@ namespace gaol_core {
gaol_exception(f,l,e) {}
invalid_action_error(const char* f, unsigned l, const std::string& e) :
gaol_exception(f,l,e) {}

private:
const char* class_name() const noexcept override {
return "invalid_action_error";
}
};

/*!
Expand Down
30 changes: 20 additions & 10 deletions manual/v5/gaol.tex
Original file line number Diff line number Diff line change
Expand Up @@ -4808,9 +4808,11 @@ \subsection{The \code{gaol\_exception} exception}\label{sec:gaol_exception}
consequence, the constructors for \code{gaol\_exception} are as follows:

\defmethodx{gaol\_exception}{}{gaol\_exception}{(const char* \var{f}, unsigned \var{l})}
\begin{defmethod}{gaol\_exception}{}{gaol\_exception}{(const char* \var{f}, unsigned \var{l}, \\\hbox{}\hfill const char* \var{e})}
\defmethodx{gaol\_exception}{}{gaol\_exception}{(const char* \var{f}, unsigned \var{l}, \\\hbox{}\hfill const char* \var{e})}
\begin{defmethod}{gaol\_exception}{}{gaol\_exception}{(const char* \var{f}, unsigned \var{l}, \\\hbox{}\hfill const std::string\& \var{e})}
Constructs a \code{gaol\_exception} being thrown from file \var{f} at
line \var{l}. The second form permits adding some explanatory string \var{e}.
line \var{l}. The second and third forms permit adding some explanatory
string \var{e}. A null pointer \var{e} is no explanation.\newinvfive
\end{defmethod}

\noindent
Expand All @@ -4830,8 +4832,10 @@ \subsection{The \code{gaol\_exception} exception}\label{sec:gaol_exception}
\end{defmethod}

\begin{defmethod}{gaol\_exception}{const char*}{what}{(void) const noexcept}
Returns the explanation, as a C string, or the text \code{gaol\_exception}
if no additional information was provided, so that it is never empty. It
Returns the explanation, as a C string, or the name of the class of the
exception (\code{gaol\_exception}, \code{input\_format\_error}\ldots) if no
additional information was provided or if the explanation starts with a
NUL character, so that it is never empty. It
overrides the \code{what()} of \code{std::exception}: a handler of
\code{std::exception}, and the message of an exception that nothing catches,
now say what went wrong, where GAOL~4 gave \code{std::exception} whatever the
Expand All @@ -4851,9 +4855,11 @@ \subsection{The \code{input\_format\_error} exception}
than the ones for \code{gaol\_exception}, namely:

\defmethodx{input\_format\_error}{}{input\_format\_error}{(const char* \var{f}, unsigned \var{l})}
\begin{defmethod}{input\_format\_error}{}{input\_format\_error}{(const char* \var{f}, unsigned \var{l}, const char* \var{e})}
\defmethodx{input\_format\_error}{}{input\_format\_error}{(const char* \var{f}, unsigned \var{l}, const char* \var{e})}
\begin{defmethod}{input\_format\_error}{}{input\_format\_error}{(const char* \var{f}, unsigned \var{l}, \\\hbox{}\hfill const std::string\& \var{e})}
Constructs an \code{input\_format\_error} being thrown from file \var{f} at
line \var{l}. The second form permits adding some explanatory string \var{e}.
line \var{l}. The second and third forms permit adding some explanatory
string \var{e}.
\end{defmethod}
The methods of the class are inherited from \code{gaol\_exception}
(\pxref{sec:gaol_exception}).
Expand All @@ -4879,9 +4885,11 @@ \subsection{The \code{unavailable\_feature\_error} exception}
than the ones for \code{gaol\_exception}, namely:

\defmethodx{unavailable\_feature\_error}{}{unavailable\_feature\_error}{\\\hbox{}\hfill (const char* \var{f}, unsigned \var{l})}
\begin{defmethod}{unavailable\_feature\_error}{}{unavailable\_feature\_error}{\\\hbox{}\hfill (const char* \var{f}, unsigned \var{l}, const char* \var{e})}
\defmethodx{unavailable\_feature\_error}{}{unavailable\_feature\_error}{\\\hbox{}\hfill (const char* \var{f}, unsigned \var{l}, const char* \var{e})}
\begin{defmethod}{unavailable\_feature\_error}{}{unavailable\_feature\_error}{\\\hbox{}\hfill (const char* \var{f}, unsigned \var{l}, const std::string\& \var{e})}
Constructs an \code{unavailable\_feature\_error} being thrown from file \var{f} at
line \var{l}. The second form permits adding some explanatory string \var{e}.
line \var{l}. The second and third forms permit adding some explanatory
string \var{e}.
\end{defmethod}

The methods of the class are inherited from \code{gaol\_exception} (\pxref{sec:gaol_exception}).
Expand All @@ -4897,9 +4905,11 @@ \subsection{The \code{invalid\_action\_error} exception}
than the ones for \code{gaol\_exception}, namely:

\defmethodx{invalid\_action\_error}{}{invalid\_action\_error}{(const char* \var{f},\\\hbox{}\hfill unsigned \var{l})}
\begin{defmethod}{invalid\_action\_error}{}{invalid\_action\_error}{(const char* \var{f},\\\hbox{}\hfill unsigned \var{l}, const char* \var{e})}
\defmethodx{invalid\_action\_error}{}{invalid\_action\_error}{(const char* \var{f},\\\hbox{}\hfill unsigned \var{l}, const char* \var{e})}
\begin{defmethod}{invalid\_action\_error}{}{invalid\_action\_error}{(const char* \var{f},\\\hbox{}\hfill unsigned \var{l}, const std::string\& \var{e})}
Constructs an \code{invalid\_action\_error} being thrown from file \var{f} at
line \var{l}. The second form permits adding some explanatory string \var{e}.
line \var{l}. The second and third forms permit adding some explanatory
string \var{e}.
\end{defmethod}

The methods of the class are inherited from \code{gaol\_exception} (\pxref{sec:gaol_exception}).
Expand Down
33 changes: 24 additions & 9 deletions tests/expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,38 +378,53 @@ namespace
libc++ whatever went wrong: the text that a handler of std::exception
printed, and the one that a program ends with when nothing catches the
exception ("what(): std::exception"), where explanation() had the
explanation. what() is now the explanation, or "gaol_exception" where
there is none, and operator<< writes the explanation once, where it
wrote what() and the explanation. The exceptions are built here with a
known explanation, so that what they say does not depend on the text of
the reader. */
explanation. what() is now the explanation, or the name of the class
where there is none, and operator<< writes the explanation once, where
it wrote what() and the explanation. A null pointer is no explanation,
where GAOL 4 built a std::string from it (undefined behavior), and an
explanation whose C string is empty (a NUL first) gives the name of the
class too, so that what() is never an empty text. The exceptions are
built here with a known explanation, so that what they say does not
depend on the text of the reader. */
template<class Exception>
void says(const char* name)
{
const unsigned line = __LINE__;
const char* const no_text = nullptr;
const Exception with_text(__FILE__, line, "boom");
const Exception with_string(__FILE__, line, std::string("boom"));
const Exception without(__FILE__, line);
const Exception with_null(__FILE__, line, no_text);
const Exception with_nul_first(__FILE__, line, std::string("\0boom", 5));

// Through the base class, as a handler of std::exception reads them
const std::exception& a = with_text;
const std::exception& b = with_string;
const std::exception& c = without;
const std::exception& d = with_null;
const std::exception& z = with_nul_first;
check("exception: what() is the explanation, given as a text",
std::string(a.what()) == "boom", [&] { return std::string(name) + ": \"" + a.what() + "\""; });
check("exception: what() is the explanation, given as a string",
std::string(b.what()) == "boom", [&] { return std::string(name) + ": \"" + b.what() + "\""; });
check("exception: what() says gaol_exception where there is no explanation",
std::string(c.what()) == "gaol_exception", [&] { return std::string(name) + ": \"" + c.what() + "\""; });
check("exception: what() is the name of the class where there is no explanation",
std::string(c.what()) == name, [&] { return std::string(name) + ": \"" + c.what() + "\""; });
check("exception: a null pointer is no explanation",
with_null.explanation().empty() && std::string(d.what()) == name,
[&] { return std::string(name) + ": \"" + with_null.explanation() + "\", what() \"" + d.what() + "\""; });
check("exception: what() is the name of the class where the explanation starts with a NUL",
std::string(z.what()) == name, [&] { return std::string(name) + ": \"" + z.what() + "\""; });

const std::string where = std::string(__FILE__) + ", line " + std::to_string(line) + ": exception thrown";
std::ostringstream shown, shown_without;
std::ostringstream shown, shown_without, shown_null;
shown << with_text;
shown_without << without;
shown_null << with_null;
check("exception: operator<< writes the file, the line and the explanation, once",
shown.str() == where + ": boom", [&] { return std::string(name) + ": " + shown.str(); });
check("exception: operator<< writes no explanation where there is none",
shown_without.str() == where, [&] { return std::string(name) + ": " + shown_without.str(); });
shown_without.str() == where && shown_null.str() == where,
[&] { return std::string(name) + ": " + shown_without.str() + " | " + shown_null.str(); });
}

/* f, which has to throw an exception of GAOL, caught as a std::exception
Expand Down
38 changes: 27 additions & 11 deletions todo-notes/synthese.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,33 @@ doc/differences.md:
place: after the bullet on `operator>>`.)
```

In `configure-clean`: **no**. To adapt once S.14 is done: without explanation
(or with an empty one) `what()` will return the name of the derived class
(`input_format_error`...), not `gaol_exception`.
In `configure-clean`: **no**. Point S (the 14) has changed what `what()` gives
without explanation, and a null explanation: take these texts instead.

ChangeLog:

```text
* gaol/gaol_exceptions.h, gaol/gaol_exceptions.cpp: gaol_exception::what() returns the explanation, or the name of the class (gaol_exception, input_format_error...) if there is none or if it starts with a NUL, where GAOL 4 left "std::exception";
a null const char* explanation is none, where GAOL 4 built a std::string from it; operator<< of the exceptions writes the explanation once.
```

doc/differences.md:

```text
- **`what()` of GAOL's exceptions is their explanation.** `gaol_exception`
overrides `what()`: it returns the explanation, or the name of the class of
the exception (`gaol_exception`, `input_format_error`,
`unavailable_feature_error` or `invalid_action_error`) where there is none
or where it starts with a NUL, so that `catch (const std::exception& e)` and
the message of an exception that nothing catches say what went wrong. GAOL
left the `what()` of `std::exception`, which is `std::exception` with
libstdc++ and libc++, whatever the error. A null pointer given as the
explanation is no explanation: GAOL built a `std::string` from it, which is
undefined behavior. `operator<<` of an exception writes the explanation
once, `file, line n: exception thrown: explanation`; it wrote `what()` next
to it, `file, line n: exception std::exception thrown: explanation`.
(Suggested place: after the bullet on `operator>>`.)
```

### `15.md` (#46) — point 15, `operator>>` on an empty line

Expand Down Expand Up @@ -1213,14 +1237,6 @@ Nothing (the measurements are in TODO M.9 and PR #36).

---

### `14.md` (#32) — point 14, `what()` of GAOL's exceptions

- `operator<<` of an exception still calls `explanation()` twice, copying the
string twice (`gaol/gaol_exceptions.cpp` l.86-87). Harmless; could be
tidied while S.14 edits that file.

---

### `15.md` (#46) — point 15, `operator>>` on an empty line

- Not in the ledger, possibly worth a clause in the synthesis: interactive
Expand Down
Loading