Merge lp:~michihenning/unity-api/add-ExceptionImplBase into lp:unity-api

Proposed by Michi Henning
Status: Merged
Approved by: Jussi Pakkanen
Approved revision: 57
Merged at revision: 59
Proposed branch: lp:~michihenning/unity-api/add-ExceptionImplBase
Merge into: lp:unity-api
Diff against target: 1556 lines (+578/-612)
14 files modified
include/unity/Exception.h (+13/-21)
include/unity/ExceptionImplBase.h (+65/-0)
include/unity/UnityExceptions.h (+7/-21)
include/unity/internal/ExceptionImpl.h (+0/-60)
include/unity/internal/UnityExceptionsImpl.h (+0/-95)
src/unity/CMakeLists.txt (+1/-0)
src/unity/Exception.cpp (+13/-13)
src/unity/ExceptionImplBase.cpp (+310/-0)
src/unity/UnityExceptions.cpp (+47/-15)
src/unity/internal/CMakeLists.txt (+0/-2)
src/unity/internal/ExceptionImpl.cpp (+0/-244)
src/unity/internal/UnityExceptionsImpl.cpp (+0/-126)
src/unity/util/Daemon.cpp (+7/-0)
test/gtest/unity/Exceptions_test.cpp (+115/-15)
To merge this branch: bzr merge lp:~michihenning/unity-api/add-ExceptionImplBase
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Jussi Pakkanen (community) Approve
Review via email: mp+169337@code.launchpad.net

Commit message

Removed unity::internal::ExceptionImpl and replaced it with unity::ExceptionImplBase.
This allows clients of unity-api to derive their own exceptions for unity::Exception without exposing any implementation details (so the ABI won't break) and still take advantage of implementation inheritance without having to reimplement all the functionality of the base class

Description of the change

Removed unity::internal::ExceptionImpl and replaced it with unity::ExceptionImplBase.
This allows clients of unity-api to derive their own exceptions for unity::Exception without exposing any implementation details (so the ABI won't break) and still take advantage of implementation inheritance without having to reimplement all the functionality of the base class.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

LGTM.

Under what circumstances would clients want to derive our exceptions, though? For their internal use they would probably use their own types. The only reason I can think of if they need to inject exceptions back to us and expect us to act on them in some way. Which seems a bit weird.

review: Approve
Revision history for this message
Michi Henning (michihenning) wrote :

> LGTM.
>
> Under what circumstances would clients want to derive our exceptions, though?

For example, unity-scopes-api is a client of unity-api. I need a MiddlewareException so I can catch a specific group of exceptions from the middleware layer selectively. I want to derive that exception from unity::Exception, so *all* exceptions ultimately are unity::exception or std::exception (which is a base for unity::exception). But that means I have to be able to derive form unity::exception without using the unity::internal namespace. (The previous design made that impossible.)

> For their internal use they would probably use their own types.

Yes, clients are free to do whatever they want with their own exceptions. But it's really nice to be able to say "catch any scope exception" or "catch any Unity API exception" for structured error handling.

And the functionality in unity::exception is kind of neat. Especially the nesting mechanism that preserves the original exception when an exception is re-thrown as something else is nice. It has zero API footprint and does the work quietly behind the scenes.

> The only
> reason I can think of if they need to inject exceptions back to us and expect
> us to act on them in some way. Which seems a bit weird.

There might a few places where we may expect scope to tell us something specific, in which case we can tell the author "you must throw X, if Y happens." But I don't expect there to many such cases, if any.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
57. By Michi Henning

Merged trunk.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Jenkins is happy with this now. Can someone top-approve please?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/unity/Exception.h'
2--- include/unity/Exception.h 2013-06-12 22:07:05 +0000
3+++ include/unity/Exception.h 2013-06-17 04:58:28 +0000
4@@ -20,6 +20,7 @@
5 #define UNITY_EXCEPTION_H
6
7 #include <unity/SymbolExport.h>
8+#include <unity/util/NonCopyable.h>
9
10 #include <exception>
11 #include <string>
12@@ -28,15 +29,7 @@
13 namespace unity
14 {
15
16-namespace internal
17-{
18-class ExceptionImpl;
19-}
20-
21-// TODO: It probably would be best to split this into a base and ImplBase, with the derived part in the internal
22-// namespace and declaring self(), the constructor, and pimpl(). That's because the Impl for the exception
23-// is in the internal namespace, so public API clients can use Exception instances, but they cannot
24-// create new derived exceptions because the Impl classes are internal to Unity.
25+class ExceptionImplBase;
26
27 /**
28 \brief Abstract base class for all Unity exceptions.
29@@ -126,14 +119,6 @@
30 */
31 virtual char const* what() const noexcept = 0;
32
33- virtual std::string reason() const;
34-
35- virtual std::string to_string(std::string const& indent = " ") const;
36- virtual std::string to_string(int indent_level, std::string const& indent) const;
37-
38- std::exception_ptr remember(std::exception_ptr earlier_exception);
39- std::exception_ptr get_earlier() const noexcept;
40-
41 /**
42 \brief Returns a <code>std::exception_ptr</code> to <code>this</code>.
43
44@@ -142,15 +127,22 @@
45 */
46 virtual std::exception_ptr self() const = 0;
47
48+ std::string reason() const;
49+
50+ std::string to_string(std::string const& indent = " ") const;
51+ std::string to_string(int indent_level, std::string const& indent) const;
52+
53+ std::exception_ptr remember(std::exception_ptr earlier_exception);
54+ std::exception_ptr get_earlier() const noexcept;
55+
56 protected:
57 //! @cond
58- Exception(std::shared_ptr<internal::ExceptionImpl> const& derived);
59- internal::ExceptionImpl* pimpl() const noexcept; // No need to reimplement in derived
60+ Exception(std::shared_ptr<ExceptionImplBase> const& derived);
61 //! @endcond
62+ ExceptionImplBase* pimpl() const noexcept;
63
64 private:
65- std::shared_ptr<internal::ExceptionImpl> p_; // shared_ptr instead of unique_ptr because
66- // exceptions must be copyable
67+ std::shared_ptr<ExceptionImplBase> p_; // shared_ptr instead of unique_ptr because exceptions must be copyable
68 };
69
70 } // namespace unity
71
72=== added file 'include/unity/ExceptionImplBase.h'
73--- include/unity/ExceptionImplBase.h 1970-01-01 00:00:00 +0000
74+++ include/unity/ExceptionImplBase.h 2013-06-17 04:58:28 +0000
75@@ -0,0 +1,65 @@
76+/*
77+ * Copyright (C) 2013 Canonical Ltd
78+ *
79+ * This program is free software: you can redistribute it and/or modify
80+ * it under the terms of the GNU General Public License version 3 as
81+ * published by the Free Software Foundation.
82+ *
83+ * This program is distributed in the hope that it will be useful,
84+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
85+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
86+ * GNU General Public License for more details.
87+ *
88+ * You should have received a copy of the GNU General Public License
89+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
90+ *
91+ * Authored by: Michi Henning <michi.henning@canonical.com>
92+ */
93+
94+#ifndef UNITY_EXCEPTIONIMPLBASE_H
95+#define UNITY_EXCEPTIONIMPLBASE_H
96+
97+#include <unity/SymbolExport.h>
98+#include <unity/util/NonCopyable.h>
99+
100+#include <exception>
101+#include <string>
102+#include <memory>
103+
104+namespace unity
105+{
106+
107+namespace internal
108+{
109+class ExceptionData;
110+}
111+
112+class Exception;
113+
114+/**
115+\brief Implementation base exception class for API clients to derive new pimpl'd exception types from.
116+\sa unity::Exception
117+*/
118+
119+class UNITY_API ExceptionImplBase : public util::NonCopyable
120+{
121+public:
122+ explicit ExceptionImplBase(Exception const* owner, std::string const& reason);
123+ //! @cond
124+ virtual ~ExceptionImplBase() noexcept;
125+ //! @endcond
126+
127+ std::string reason() const;
128+
129+ std::string to_string(std::nested_exception const* nested, int indent_level, std::string const& indent) const;
130+
131+ std::exception_ptr set_earlier(std::exception_ptr earlier_exception);
132+ std::exception_ptr get_earlier() const noexcept;
133+
134+private:
135+ std::shared_ptr<internal::ExceptionData> p_; // shared_ptr (not unique_ptr) because ExceptionData is incomplete
136+};
137+
138+} // namespace unity
139+
140+#endif
141
142=== modified file 'include/unity/UnityExceptions.h'
143--- include/unity/UnityExceptions.h 2013-06-12 22:07:05 +0000
144+++ include/unity/UnityExceptions.h 2013-06-17 04:58:28 +0000
145@@ -24,11 +24,6 @@
146 namespace unity
147 {
148
149-namespace internal
150-{
151-class InvalidArgumentExceptionImpl;
152-}
153-
154 /**
155 \brief Exception to indicate that an invalid argument was passed to a function, such as passing <code>nullptr</code>
156 when the function expects the argument to be non-null.
157@@ -59,11 +54,6 @@
158 virtual std::exception_ptr self() const override;
159 };
160
161-namespace internal
162-{
163-class LogicExceptionImpl;
164-}
165-
166 /**
167 \brief Exception to indicate a logic error, such as driving the API incorrectly, such as calling methods
168 in the wrong worder.
169@@ -94,11 +84,6 @@
170 virtual std::exception_ptr self() const override;
171 };
172
173-namespace internal
174-{
175-class ShutdownExceptionImpl;
176-}
177-
178 /**
179 \brief Exception to indicate errors during shutdown.
180
181@@ -174,6 +159,9 @@
182 \return Returns the error number that was passed to the constructor.
183 */
184 int error() const noexcept;
185+
186+private:
187+ std::shared_ptr<internal::FileExceptionImpl> p_;
188 };
189
190 namespace internal
191@@ -217,13 +205,11 @@
192 \return Returns the error number that was passed to the constructor.
193 */
194 int error() const noexcept;
195+
196+private:
197+ //std::shared_ptr<internal::SyscallExceptionImpl> p_;
198 };
199
200-namespace internal
201-{
202-class ResourceExceptionImpl;
203-}
204-
205 /**
206 \brief Exception for miscellaneous errors, such as failure of a third-party library or hitting resource limitations.
207 */
208@@ -235,7 +221,7 @@
209 \brief Constructs the exception.
210 \param reason Further details about the cause of the exception.
211 */
212- ResourceException(std::string const& reason);
213+ explicit ResourceException(std::string const& reason);
214 //! @cond
215 ResourceException(ResourceException const&);
216 ResourceException& operator=(ResourceException const&);
217
218=== removed file 'include/unity/internal/ExceptionImpl.h'
219--- include/unity/internal/ExceptionImpl.h 2013-06-12 22:07:05 +0000
220+++ include/unity/internal/ExceptionImpl.h 1970-01-01 00:00:00 +0000
221@@ -1,60 +0,0 @@
222-/*
223- * Copyright (C) 2013 Canonical Ltd
224- *
225- * This program is free software: you can redistribute it and/or modify
226- * it under the terms of the Lesser GNU General Public License version 3 as
227- * published by the Free Software Foundation.
228- *
229- * This program is distributed in the hope that it will be useful,
230- * but WITHOUT ANY WARRANTY; without even the implied warranty of
231- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
232- * Lesser GNU General Public License for more details.
233- *
234- * You should have received a copy of the Lesser GNU General Public License
235- * along with this program. If not, see <http://www.gnu.org/licenses/>.
236- *
237- * Authored by: Michi Henning <michi.henning@canonical.com>
238- */
239-
240-#ifndef UNITY_INTERNAL_EXCEPTIONIMPL_H
241-#define UNITY_INTERNAL_EXCEPTIONIMPL_H
242-
243-#include <unity/util/NonCopyable.h>
244-
245-#include <string>
246-#include <exception>
247-
248-namespace unity
249-{
250-
251-class Exception;
252-
253-namespace internal
254-{
255-
256-class ExceptionImpl : private util::NonCopyable
257-{
258-public:
259- explicit ExceptionImpl(std::string const& reason);
260-
261- virtual char const* what() const noexcept = 0;
262- virtual std::string reason() const;
263- virtual std::string to_string(std::nested_exception const& nested, int indent_level, std::string const& indent) const;
264-
265- std::exception_ptr remember(unity::Exception const* env, std::exception_ptr earlier_exception);
266- std::exception_ptr remember(unity::Exception const* env, Exception const& earlier_exception);
267- std::exception_ptr get_earlier() const noexcept;
268-
269-protected:
270- virtual ~ExceptionImpl() noexcept;
271-
272-private:
273- std::string const reason_;
274- std::exception_ptr previous_;
275-};
276-
277-} // namespace internal
278-
279-} // namespace unity
280-
281-#endif
282
283=== removed file 'include/unity/internal/UnityExceptionsImpl.h'
284--- include/unity/internal/UnityExceptionsImpl.h 2013-06-12 22:07:05 +0000
285+++ include/unity/internal/UnityExceptionsImpl.h 1970-01-01 00:00:00 +0000
286@@ -1,95 +0,0 @@
287-/*
288- * Copyright (C) 2012 Canonical Ltd
289- *
290- * This program is free software: you can redistribute it and/or modify
291- * it under the terms of the Lesser GNU General Public License version 3 as
292- * published by the Free Software Foundation.
293- *
294- * This program is distributed in the hope that it will be useful,
295- * but WITHOUT ANY WARRANTY; without even the implied warranty of
296- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
297- * Lesser GNU General Public License for more details.
298- *
299- * You should have received a copy of the Lesser GNU General Public License
300- * along with this program. If not, see <http://www.gnu.org/licenses/>.
301- *
302- * Authored by: Michi Henning <michi.henning@canonical.com>
303- */
304-
305-#ifndef UNITY_INTERNAL_UNITYEXCEPTIONSIMPL_H
306-#define UNITY_INTERNAL_UNITYEXCEPTIONSIMPL_H
307-
308-#include <unity/internal/ExceptionImpl.h>
309-
310-#include <string>
311-#include <exception>
312-
313-namespace unity
314-{
315-
316-class Exception;
317-
318-namespace internal
319-{
320-
321-class InvalidArgumentExceptionImpl : public ExceptionImpl
322-{
323-public:
324- InvalidArgumentExceptionImpl(std::string const& reason);
325-
326- virtual char const* what() const noexcept override;
327-};
328-
329-class LogicExceptionImpl : public ExceptionImpl
330-{
331-public:
332- LogicExceptionImpl(std::string const& reason);
333-
334- virtual char const* what() const noexcept override;
335-};
336-
337-class ShutdownExceptionImpl : public ExceptionImpl
338-{
339-public:
340- ShutdownExceptionImpl(std::string const& reason);
341-
342- virtual char const* what() const noexcept override;
343-};
344-
345-class FileExceptionImpl : public ExceptionImpl
346-{
347-public:
348- FileExceptionImpl(std::string const& reason, int err);
349-
350- virtual char const* what() const noexcept override;
351- int error() const noexcept;
352-
353-private:
354- int errno_;
355-};
356-
357-class SyscallExceptionImpl : public ExceptionImpl
358-{
359-public:
360- SyscallExceptionImpl(std::string const& reason, int err);
361-
362- virtual char const* what() const noexcept override;
363- int error() const noexcept;
364-
365-private:
366- int const errno_;
367-};
368-
369-class ResourceExceptionImpl : public ExceptionImpl
370-{
371-public:
372- ResourceExceptionImpl(std::string const& reason);
373-
374- virtual char const* what() const noexcept override;
375-};
376-
377-} // namespace internal
378-
379-} // namespace unity
380-
381-#endif
382
383=== modified file 'src/unity/CMakeLists.txt'
384--- src/unity/CMakeLists.txt 2013-05-13 11:39:49 +0000
385+++ src/unity/CMakeLists.txt 2013-06-17 04:58:28 +0000
386@@ -4,6 +4,7 @@
387
388 set(UNITY_SRC
389 ${CMAKE_CURRENT_SOURCE_DIR}/Exception.cpp
390+ ${CMAKE_CURRENT_SOURCE_DIR}/ExceptionImplBase.cpp
391 ${CMAKE_CURRENT_SOURCE_DIR}/UnityExceptions.cpp
392 )
393
394
395=== modified file 'src/unity/Exception.cpp'
396--- src/unity/Exception.cpp 2013-06-12 22:07:05 +0000
397+++ src/unity/Exception.cpp 2013-06-17 04:58:28 +0000
398@@ -17,7 +17,7 @@
399 */
400
401 #include <unity/Exception.h>
402-#include <unity/internal/ExceptionImpl.h>
403+#include <unity/ExceptionImplBase.h>
404
405 using namespace std;
406
407@@ -27,7 +27,7 @@
408 //! @cond
409
410 Exception::
411-Exception(shared_ptr<internal::ExceptionImpl> const& derived)
412+Exception(shared_ptr<ExceptionImplBase> const& derived)
413 : p_(derived)
414 {
415 }
416@@ -59,7 +59,7 @@
417 }
418
419 /**
420-Returns a string describing the exception, including any exceptions that were nested or chained.
421+\brief Returns a string describing the exception, including any exceptions that were nested or chained.
422
423 Nested exceptions are indented according to their nesting level. If the exception contains chained
424 exceptions, these are shown in oldest-to-newest order.
425@@ -74,11 +74,11 @@
426 Exception::
427 to_string(std::string const& indent) const
428 {
429- return p_->to_string(*this, 0, indent);
430+ return p_->to_string(this, 0, indent);
431 }
432
433 /**
434-Returns a string describing the exception, including any exceptions that were nested or chained.
435+\brief Returns a string describing the exception, including any exceptions that were nested or chained.
436
437 Nested exceptions are indented according to their nesting level. If the exception contains chained
438 exceptions, these are shown in oldest-to-newest order.
439@@ -97,11 +97,11 @@
440 Exception::
441 to_string(int indent_level, std::string const& indent) const
442 {
443- return p_->to_string(*this, indent_level, indent);
444+ return p_->to_string(this, indent_level, indent);
445 }
446
447 /**
448-Adds an exception to the exception history chain.
449+\brief Adds an exception to the exception history chain.
450
451 \param earlier_exception The parameter must be a <code>nullptr</code> or a <code>std::exception_ptr</code>
452 to an exception that was remembered earlier. This allows a sequence of exceptions to be remembered without
453@@ -116,11 +116,11 @@
454 Exception::
455 remember(exception_ptr earlier_exception)
456 {
457- return p_->remember(this, earlier_exception);
458+ return p_->set_earlier(earlier_exception);
459 }
460
461 /**
462-Returns the previous exception.
463+\brief Returns the previous exception.
464 \return Returns the next-older remembered exception, or <code>nullptr</code>, if none.
465 */
466
467@@ -131,15 +131,15 @@
468 return p_->get_earlier();
469 }
470
471-//! @cond
472+/**
473+\brief Returns a pointer to to the implementation instance.
474+*/
475
476-internal::ExceptionImpl*
477+ExceptionImplBase*
478 Exception::
479 pimpl() const noexcept
480 {
481 return p_.get();
482 }
483
484-//! @endcond
485-
486 } // namespace unity
487
488=== added file 'src/unity/ExceptionImplBase.cpp'
489--- src/unity/ExceptionImplBase.cpp 1970-01-01 00:00:00 +0000
490+++ src/unity/ExceptionImplBase.cpp 2013-06-17 04:58:28 +0000
491@@ -0,0 +1,310 @@
492+/*
493+ * Copyright (C) 2012 Canonical Ltd
494+ *
495+ * This program is free software: you can redistribute it and/or modify
496+ * it under the terms of the GNU General Public License version 3 as
497+ * published by the Free Software Foundation.
498+ *
499+ * This program is distributed in the hope that it will be useful,
500+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
501+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
502+ * GNU General Public License for more details.
503+ *
504+ * You should have received a copy of the GNU General Public License
505+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
506+ *
507+ * Authored by: Michi Henning <michi.henning@canonical.com>
508+ */
509+
510+#include <unity/ExceptionImplBase.h>
511+#include <unity/Exception.h>
512+
513+using namespace std;
514+
515+namespace unity
516+{
517+
518+namespace internal
519+{
520+
521+class ExceptionData
522+{
523+public:
524+ ExceptionData(Exception const* owner, string const& reason)
525+ : owner_(owner)
526+ , reason_(reason)
527+ , earlier_(nullptr)
528+ {
529+ }
530+
531+ ExceptionData(ExceptionData const&) = delete;
532+ ExceptionData& operator=(ExceptionData const&) = delete;
533+
534+ Exception const* owner_;
535+ string reason_;
536+ std::exception_ptr earlier_;
537+};
538+
539+}
540+
541+/**
542+\brief Instantiates an implementation instance for a unity::Exception.
543+\param owner This must be set to the `this` pointer of the exception that owns this implementation.
544+\param reason Further details about the cause of the exception.
545+*/
546+
547+ExceptionImplBase::
548+ExceptionImplBase(Exception const* owner, string const& reason)
549+ : p_(make_shared<internal::ExceptionData>(owner, reason))
550+{
551+}
552+
553+//! @cond
554+
555+ExceptionImplBase::
556+~ExceptionImplBase() noexcept = default;
557+
558+namespace
559+{
560+
561+//
562+// Return the margin string for the indent level and indent.
563+//
564+
565+string
566+get_margin(int indent_level, string const& indent)
567+{
568+ string margin;
569+ for (int i = 0; i < indent_level; ++i)
570+ {
571+ margin += indent;
572+ }
573+ return margin;
574+}
575+
576+//
577+// Follow the nested exceptions that were rethrown along the call stack, printing them into s.
578+//
579+
580+void
581+print_name_and_reason(string& s, nested_exception const* nested)
582+{
583+ // Add the what() string if this is a std::exception.
584+
585+ std::exception const* std_exception = dynamic_cast<std::exception const*>(nested);
586+ if (std_exception)
587+ {
588+ s += std_exception->what();
589+ }
590+
591+ // Add the reason if this is a unity::Exception.
592+
593+ unity::Exception const* unity_exception = dynamic_cast<unity::Exception const*>(nested);
594+ if (unity_exception)
595+ {
596+ string reason = unity_exception->reason();
597+ if (!reason.empty())
598+ {
599+ s += ": " + reason;
600+ }
601+ }
602+
603+ // Append info about unknown std::exception and std::nested_exception.
604+
605+ if (!unity_exception)
606+ {
607+ if (std_exception)
608+ {
609+ s += " (derived from std::exception and std::nested_exception)";
610+ }
611+ else
612+ {
613+ s += "std::nested_exception";
614+ }
615+ }
616+}
617+
618+void
619+follow_nested(string& s, nested_exception const* nested, int indent_level, std::string const& indent)
620+{
621+ if (nested->nested_ptr())
622+ {
623+ string margin = get_margin(indent_level, indent);
624+
625+ s += ":\n";
626+ try
627+ {
628+ nested->rethrow_nested();
629+ }
630+ catch (std::nested_exception const& e)
631+ {
632+ unity::Exception const* ge = dynamic_cast<unity::Exception const*>(&e);
633+ if (ge)
634+ {
635+ s += ge->to_string(indent_level + 1, indent);
636+ }
637+ else
638+ {
639+ s += margin + indent;
640+ print_name_and_reason(s, &e);
641+ follow_nested(s, &e, indent_level + 1, indent);
642+ }
643+ }
644+ catch (std::exception const& e)
645+ {
646+ s += margin + indent;
647+ s += e.what(); // Can show only what() for std::exception.
648+ }
649+ catch (...)
650+ {
651+ s += margin + indent;
652+ s += "unknown exception"; // Best we can do for an exception whose type we don't know.
653+ }
654+ }
655+}
656+
657+//
658+// Follow the history chain and print each exception in the chain.
659+//
660+
661+void
662+follow_history(string& s, int& count, unity::Exception const* e, int indent_level, std::string const& indent)
663+{
664+ if (!e->get_earlier())
665+ {
666+ count = 1; // We have reached the oldest exception; set exception generation count and terminate recursion.
667+ }
668+ else
669+ {
670+ try
671+ {
672+ rethrow_exception(e->get_earlier());
673+ }
674+ catch (unity::Exception const& e)
675+ {
676+ // Recurse along the chain until we hit the end, then, as we pop back up the levels, we increment the
677+ // count and print it as a generation number for the exception information.
678+ // A bit like the "kicks" in "Inception", except that the deepest level is level 1...
679+
680+ follow_history(s, count, &e, indent_level, indent);
681+ }
682+ ++count;
683+ }
684+
685+ // Show info for this exception.
686+
687+ s += "\n" + get_margin(indent_level, indent) + "Exception #";
688+ s += to_string(count) + ":\n";
689+ s += get_margin(indent_level, indent) + indent;
690+ print_name_and_reason(s, e);
691+ follow_nested(s, e, indent_level + 1, indent);
692+}
693+
694+} // namespace
695+
696+//! @endcond
697+
698+/**
699+\brief Returns the reason set by the derived class's constructor (empty string if none).
700+
701+Derived classes should include any other state information, such as the value of data members or
702+other relevant detail in the <code>reason</code> string they pass to the protected constructor.
703+*/
704+
705+string
706+ExceptionImplBase::
707+reason() const
708+{
709+ return p_->reason_;
710+}
711+
712+/**
713+\brief Returns a string describing the exception, including any exceptions that were nested or chained.
714+
715+Nested exceptions are indented according to their nesting level. If the exception contains chained
716+exceptions, these are shown in oldest-to-newest order.
717+
718+\param nested This must be set to the `this` pointer of the exception that owns this implementation.
719+
720+\param indent_level This controls the indent level. The value <code>0</code> indicates
721+ the outermost level (no indent).
722+\param indent This controls the amount of indenting per level. The default indent is four spaces.
723+\return The string describing the exception.
724+*/
725+
726+string
727+ExceptionImplBase::
728+to_string(nested_exception const* nested, int indent_level, string const& indent) const
729+{
730+ string margin = get_margin(indent_level, indent);
731+ string s = margin;
732+ s += dynamic_cast<const std::exception*>(nested)->what();
733+
734+ string r = reason();
735+ if (!r.empty())
736+ {
737+ s += ": " + r;
738+ }
739+
740+ // Check whether there is an exception history and print each exception in the history.
741+
742+ unity::Exception const* unity_exception(dynamic_cast<unity::Exception const*>(nested));
743+ if (unity_exception && unity_exception->get_earlier())
744+ {
745+ s += "\n" + margin + indent + "Exception history:";
746+ try
747+ {
748+ rethrow_exception(unity_exception->get_earlier());
749+ }
750+ catch (unity::Exception const& e)
751+ {
752+ int count;
753+ follow_history(s, count, &e, indent_level + 2, indent);
754+ }
755+ }
756+
757+ // Print this and any nested exceptions.
758+
759+ follow_nested(s, nested, indent_level, indent);
760+
761+ return s;
762+}
763+
764+/**
765+\brief Adds an exception to the exception history chain.
766+
767+\param earlier_exception The parameter must be a <code>nullptr</code> or a <code>std::exception_ptr</code>
768+to an exception that was remembered earlier. This allows a sequence of exceptions to be remembered without
769+having to throw them and is useful for example, in shutdown scenarios where any one of a sequence of steps
770+can fail, but we want to continue and try all the following steps and only throw after all of them have been
771+tried. In this case, each step that fails can add itself to the sequence of remembered exceptions, and finally
772+throw something like <code>ShutdownException</code>.
773+\return A <code>std::exception_ptr</code> to <code>this</code>.
774+*/
775+
776+exception_ptr
777+ExceptionImplBase::
778+set_earlier(exception_ptr earlier_exception)
779+{
780+ // Doesn't prevent loops, but protects against accidental self-assignment.
781+
782+ if (p_->earlier_ != earlier_exception)
783+ {
784+ p_->earlier_ = earlier_exception;
785+ }
786+ return p_->owner_->self();
787+}
788+
789+/**
790+\brief Returns the previous exception.
791+\return Returns the next-older remembered exception, or <code>nullptr</code>, if none.
792+*/
793+
794+exception_ptr
795+ExceptionImplBase::
796+get_earlier() const noexcept
797+{
798+ return p_->earlier_;
799+}
800+
801+} // namespace unity
802
803=== modified file 'src/unity/UnityExceptions.cpp'
804--- src/unity/UnityExceptions.cpp 2013-06-12 22:07:05 +0000
805+++ src/unity/UnityExceptions.cpp 2013-06-17 04:58:28 +0000
806@@ -17,7 +17,7 @@
807 */
808
809 #include <unity/UnityExceptions.h>
810-#include <unity/internal/UnityExceptionsImpl.h>
811+#include <unity/ExceptionImplBase.h>
812
813 using namespace std;
814
815@@ -26,7 +26,7 @@
816
817 InvalidArgumentException::
818 InvalidArgumentException(string const& reason)
819- : Exception(make_shared<internal::InvalidArgumentExceptionImpl>(reason))
820+ : Exception(make_shared<ExceptionImplBase>(this, reason))
821 {
822 }
823
824@@ -49,7 +49,7 @@
825 InvalidArgumentException::
826 what() const noexcept
827 {
828- return dynamic_cast<internal::InvalidArgumentExceptionImpl*>(pimpl())->what();
829+ return "unity::InvalidArgumentException";
830 }
831
832 exception_ptr
833@@ -61,7 +61,7 @@
834
835 LogicException::
836 LogicException(string const& reason)
837- : Exception(make_shared<internal::LogicExceptionImpl>(reason))
838+ : Exception(make_shared<ExceptionImplBase>(this, reason))
839 {
840 }
841
842@@ -83,7 +83,7 @@
843 LogicException::
844 what() const noexcept
845 {
846- return dynamic_cast<internal::LogicExceptionImpl*>(pimpl())->what();
847+ return "unity::LogicException";
848 }
849
850 exception_ptr
851@@ -95,7 +95,7 @@
852
853 ShutdownException::
854 ShutdownException(string const& reason)
855- : Exception(make_shared<internal::ShutdownExceptionImpl>(reason))
856+ : Exception(make_shared<ExceptionImplBase>(this, reason))
857 {
858 }
859
860@@ -117,7 +117,7 @@
861 ShutdownException::
862 what() const noexcept
863 {
864- return dynamic_cast<internal::ShutdownExceptionImpl*>(pimpl())->what();
865+ return "unity::ShutdownException";
866 }
867
868 exception_ptr
869@@ -127,9 +127,25 @@
870 return make_exception_ptr(*this);
871 }
872
873+namespace internal
874+{
875+
876+class FileExceptionImpl : public unity::ExceptionImplBase
877+{
878+public:
879+ FileExceptionImpl(FileException const* owner, string const& reason, int err)
880+ : ExceptionImplBase(owner, reason + (err == 0 ? "" : " (errno = " + std::to_string(err) + ")"))
881+ , err_(err)
882+ {
883+ }
884+ int err_;
885+};
886+
887+}
888+
889 FileException::
890 FileException(string const& reason, int err)
891- : Exception(make_shared<internal::FileExceptionImpl>(reason, err))
892+ : Exception(make_shared<internal::FileExceptionImpl>(this, reason, err))
893 {
894 }
895
896@@ -151,14 +167,14 @@
897 FileException::
898 what() const noexcept
899 {
900- return dynamic_cast<internal::FileExceptionImpl*>(pimpl())->what();
901+ return "unity::FileException";
902 }
903
904 int
905 FileException::
906 error() const noexcept
907 {
908- return dynamic_cast<internal::FileExceptionImpl*>(pimpl())->error();
909+ return dynamic_cast<const internal::FileExceptionImpl*>(pimpl())->err_;
910 }
911
912 exception_ptr
913@@ -168,9 +184,25 @@
914 return make_exception_ptr(*this);
915 }
916
917+namespace internal
918+{
919+
920+class SyscallExceptionImpl : public unity::ExceptionImplBase
921+{
922+public:
923+ SyscallExceptionImpl(SyscallException const* owner, string const& reason, int err)
924+ : ExceptionImplBase(owner, reason + (reason.empty() ? "" : " ") + "(errno = " + std::to_string(err) + ")")
925+ , err_(err)
926+ {
927+ }
928+ int err_;
929+};
930+
931+}
932+
933 SyscallException::
934 SyscallException(string const& reason, int err)
935- : Exception(make_shared<internal::SyscallExceptionImpl>(reason, err))
936+ : Exception(make_shared<internal::SyscallExceptionImpl>(this, reason, err))
937 {
938 }
939
940@@ -192,14 +224,14 @@
941 SyscallException::
942 what() const noexcept
943 {
944- return dynamic_cast<internal::SyscallExceptionImpl*>(pimpl())->what();
945+ return "unity::SyscallException";
946 }
947
948 int
949 SyscallException::
950 error() const noexcept
951 {
952- return dynamic_cast<internal::SyscallExceptionImpl*>(pimpl())->error();
953+ return dynamic_cast<const internal::SyscallExceptionImpl*>(pimpl())->err_;
954 }
955
956 exception_ptr
957@@ -211,7 +243,7 @@
958
959 ResourceException::
960 ResourceException(string const& reason)
961- : Exception(make_shared<internal::ResourceExceptionImpl>(reason))
962+ : Exception(make_shared<ExceptionImplBase>(this, reason))
963 {
964 }
965
966@@ -233,7 +265,7 @@
967 ResourceException::
968 what() const noexcept
969 {
970- return dynamic_cast<internal::ResourceExceptionImpl*>(pimpl())->what();
971+ return "unity::ResourceException";
972 }
973
974 exception_ptr
975
976=== modified file 'src/unity/internal/CMakeLists.txt'
977--- src/unity/internal/CMakeLists.txt 2013-05-13 11:39:49 +0000
978+++ src/unity/internal/CMakeLists.txt 2013-06-17 04:58:28 +0000
979@@ -1,6 +1,4 @@
980 set(UNITY_INTERNAL_SRC
981- ${CMAKE_CURRENT_SOURCE_DIR}/ExceptionImpl.cpp
982- ${CMAKE_CURRENT_SOURCE_DIR}/UnityExceptionsImpl.cpp
983 )
984
985 set(UNITY_API_LIB_SRC ${UNITY_API_LIB_SRC} ${UNITY_INTERNAL_SRC} PARENT_SCOPE)
986
987=== removed file 'src/unity/internal/ExceptionImpl.cpp'
988--- src/unity/internal/ExceptionImpl.cpp 2013-06-12 22:07:05 +0000
989+++ src/unity/internal/ExceptionImpl.cpp 1970-01-01 00:00:00 +0000
990@@ -1,244 +0,0 @@
991-/*
992- * Copyright (C) 2013 Canonical Ltd
993- *
994- * This program is free software: you can redistribute it and/or modify
995- * it under the terms of the Lesser GNU General Public License version 3 as
996- * published by the Free Software Foundation.
997- *
998- * This program is distributed in the hope that it will be useful,
999- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1000- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1001- * Lesser GNU General Public License for more details.
1002- *
1003- * You should have received a copy of the Lesser GNU General Public License
1004- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1005- *
1006- * Authored by: Michi Henning <michi.henning@canonical.com>
1007- */
1008-
1009-#include <unity/internal/ExceptionImpl.h>
1010-#include <unity/Exception.h>
1011-
1012-using namespace std;
1013-
1014-namespace unity
1015-{
1016-
1017-namespace internal
1018-{
1019-
1020-ExceptionImpl::
1021-ExceptionImpl(string const& reason)
1022- : reason_(reason)
1023-{
1024-}
1025-
1026-ExceptionImpl::
1027-~ExceptionImpl() noexcept = default;
1028-
1029-string
1030-ExceptionImpl::
1031-reason() const
1032-{
1033- return reason_;
1034-}
1035-
1036-namespace
1037-{
1038-
1039-//
1040-// Return the margin string for the indent level and indent.
1041-//
1042-
1043-string
1044-get_margin(int indent_level, string const& indent)
1045-{
1046- string margin;
1047- for (int i = 0; i < indent_level; ++i)
1048- {
1049- margin += indent;
1050- }
1051- return margin;
1052-}
1053-
1054-//
1055-// Follow the nested exceptions that were rethrown along the call stack, printing them into s.
1056-//
1057-
1058-void
1059-print_name_and_reason(string& s, nested_exception const& nested)
1060-{
1061- // Add the what() string if this is a std::exception.
1062-
1063- std::exception const* std_exception = dynamic_cast<std::exception const*>(&nested);
1064- if (std_exception)
1065- {
1066- s += std_exception->what();
1067- }
1068-
1069- // Add the reason if this is a unity::Exception.
1070-
1071- unity::Exception const* unity_exception = dynamic_cast<unity::Exception const*>(&nested);
1072- if (unity_exception)
1073- {
1074- string reason = unity_exception->reason();
1075- if (!reason.empty())
1076- {
1077- s += ": " + reason;
1078- }
1079- }
1080-
1081- // Append info about unknown std::exception and std::nested_exception.
1082-
1083- if (!unity_exception)
1084- {
1085- if (std_exception)
1086- {
1087- s += " (derived from std::exception and std::nested_exception)";
1088- }
1089- else
1090- {
1091- s += "std::nested_exception";
1092- }
1093- }
1094-}
1095-
1096-void
1097-follow_nested(string& s, nested_exception const& nested, int indent_level, std::string const& indent)
1098-{
1099- if (nested.nested_ptr())
1100- {
1101- string margin = get_margin(indent_level, indent);
1102-
1103- s += ":\n";
1104- try
1105- {
1106- nested.rethrow_nested();
1107- }
1108- catch (std::nested_exception const& e)
1109- {
1110- unity::Exception const* ge = dynamic_cast<unity::Exception const*>(&e);
1111- if (ge)
1112- {
1113- s += ge->to_string(indent_level + 1, indent);
1114- }
1115- else
1116- {
1117- s += margin + indent;
1118- print_name_and_reason(s, e);
1119- follow_nested(s, e, indent_level + 1, indent);
1120- }
1121- }
1122- catch (std::exception const& e)
1123- {
1124- s += margin + indent;
1125- s += e.what(); // Can show only what() for std::exception.
1126- }
1127- catch (...)
1128- {
1129- s += margin + indent;
1130- s += "unknown exception"; // Best we can do for an exception whose type we don't know.
1131- }
1132- }
1133-}
1134-
1135-//
1136-// Follow the history chain and print each exception in the chain.
1137-//
1138-
1139-void
1140-follow_history(string& s, int& count, unity::Exception const& e, int indent_level, std::string const& indent)
1141-{
1142- if (!e.get_earlier())
1143- {
1144- count = 1; // We have reached the oldest exception; set exception generation count and terminate recursion.
1145- }
1146- else
1147- {
1148- try
1149- {
1150- rethrow_exception(e.get_earlier());
1151- }
1152- catch (unity::Exception const& e)
1153- {
1154- // Recurse along the chain until we hit the end, then, as we pop back up the levels, we increment the
1155- // count and print it as a generation number for the exception information.
1156- // A bit like the "kicks" in "Inception", except that the deepest level is level 1...
1157-
1158- follow_history(s, count, e, indent_level, indent);
1159- }
1160- ++count;
1161- }
1162-
1163- // Show info for this exception.
1164-
1165- s += "\n" + get_margin(indent_level, indent) + "Exception #";
1166- s += to_string(count) + ":\n";
1167- s += get_margin(indent_level, indent) + indent;
1168- print_name_and_reason(s, e);
1169- follow_nested(s, e, indent_level + 1, indent);
1170-}
1171-
1172-} // namespace
1173-
1174-string
1175-ExceptionImpl::
1176-to_string(nested_exception const& nested, int indent_level, string const& indent) const
1177-{
1178- string margin = get_margin(indent_level, indent);
1179- string s = margin;
1180- s += what();
1181-
1182- string r = reason();
1183- if (!r.empty())
1184- {
1185- s += ": " + r;
1186- }
1187-
1188- // Check whether there is an exception history and print each exception in the history.
1189-
1190- unity::Exception const* unity_exception(dynamic_cast<unity::Exception const*>(&nested));
1191- if (unity_exception && unity_exception->get_earlier())
1192- {
1193- s += "\n" + margin + indent + "Exception history:";
1194- try
1195- {
1196- rethrow_exception(unity_exception->get_earlier());
1197- }
1198- catch (unity::Exception const& e)
1199- {
1200- int count;
1201- follow_history(s, count, e, indent_level + 2, indent);
1202- }
1203- }
1204-
1205- // Print this and any nested exceptions.
1206-
1207- follow_nested(s, nested, indent_level, indent);
1208-
1209- return s;
1210-}
1211-
1212-exception_ptr
1213-ExceptionImpl::
1214-remember(Exception const* env, exception_ptr earlier_exception)
1215-{
1216- // Doesn't prevent loops, but protects against accidental self-assignment.
1217-
1218- if (previous_ != earlier_exception)
1219- {
1220- previous_ = earlier_exception;
1221- }
1222- return env->self();
1223-}
1224-
1225-exception_ptr
1226-ExceptionImpl::
1227-get_earlier() const noexcept
1228-{
1229- return previous_;
1230-}
1231-
1232-} // namespace internal
1233-
1234-} // namespace unity
1235
1236=== removed file 'src/unity/internal/UnityExceptionsImpl.cpp'
1237--- src/unity/internal/UnityExceptionsImpl.cpp 2013-06-12 22:07:05 +0000
1238+++ src/unity/internal/UnityExceptionsImpl.cpp 1970-01-01 00:00:00 +0000
1239@@ -1,126 +0,0 @@
1240-/*
1241- * Copyright (C) 2012 Canonical Ltd
1242- *
1243- * This program is free software: you can redistribute it and/or modify
1244- * it under the terms of the Lesser GNU General Public License version 3 as
1245- * published by the Free Software Foundation.
1246- *
1247- * This program is distributed in the hope that it will be useful,
1248- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1249- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1250- * Lesser GNU General Public License for more details.
1251- *
1252- * You should have received a copy of the Lesser GNU General Public License
1253- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1254- *
1255- * Authored by: Michi Henning <michi.henning@canonical.com>
1256- */
1257-
1258-#include <unity/internal/UnityExceptionsImpl.h>
1259-
1260-using namespace std;
1261-
1262-namespace unity
1263-{
1264-
1265-namespace internal
1266-{
1267-
1268-InvalidArgumentExceptionImpl::
1269-InvalidArgumentExceptionImpl(string const& reason)
1270- : ExceptionImpl(reason)
1271-{
1272-}
1273-
1274-char const*
1275-InvalidArgumentExceptionImpl::
1276-what() const noexcept
1277-{
1278- return "unity::InvalidArgumentException";
1279-}
1280-
1281-LogicExceptionImpl::
1282-LogicExceptionImpl(string const& reason)
1283- : ExceptionImpl(reason)
1284-{
1285-}
1286-
1287-char const*
1288-LogicExceptionImpl::
1289-what() const noexcept
1290-{
1291- return "unity::LogicException";
1292-}
1293-
1294-ShutdownExceptionImpl::
1295-ShutdownExceptionImpl(string const& reason)
1296- : ExceptionImpl(reason)
1297-{
1298-}
1299-
1300-char const*
1301-ShutdownExceptionImpl::
1302-what() const noexcept
1303-{
1304- return "unity::ShutdownException";
1305-}
1306-
1307-FileExceptionImpl::
1308-FileExceptionImpl(string const& reason, int err)
1309- : ExceptionImpl(reason + (err == 0 ? "" : " (errno = " + std::to_string(err) + ")"))
1310- , errno_(err)
1311-{
1312-}
1313-
1314-char const*
1315-FileExceptionImpl::
1316-what() const noexcept
1317-{
1318- return "unity::FileException";
1319-}
1320-
1321-int
1322-FileExceptionImpl::
1323-error() const noexcept
1324-{
1325- return errno_;
1326-}
1327-
1328-SyscallExceptionImpl::
1329-SyscallExceptionImpl(string const& reason, int err)
1330- : ExceptionImpl(reason + (reason.empty() ? "" : " ") + "(errno = " + std::to_string(err) + ")")
1331- , errno_(err)
1332-{
1333-}
1334-
1335-char const*
1336-SyscallExceptionImpl::
1337-what() const noexcept
1338-{
1339- return "unity::SyscallException";
1340-}
1341-
1342-int
1343-SyscallExceptionImpl::
1344-error() const noexcept
1345-{
1346- return errno_;
1347-}
1348-
1349-ResourceExceptionImpl::
1350-ResourceExceptionImpl(string const& reason)
1351- : ExceptionImpl(reason)
1352-{
1353-}
1354-
1355-char const*
1356-ResourceExceptionImpl::
1357-what() const noexcept
1358-{
1359- return "unity::ResourceException";
1360-}
1361-
1362-
1363-} // namespace internal
1364-
1365-} // namespace unity
1366
1367=== modified file 'src/unity/util/Daemon.cpp'
1368--- src/unity/util/Daemon.cpp 2013-06-12 22:07:05 +0000
1369+++ src/unity/util/Daemon.cpp 2013-06-17 04:58:28 +0000
1370@@ -34,6 +34,11 @@
1371 return UPtr(new Daemon());
1372 }
1373
1374+// This is covered by tests, but only when we are not building for coverage.
1375+// (Closing all file descriptors interferes with the coverage reporting.)
1376+
1377+// LCOV_EXCL_START
1378+
1379 void
1380 Daemon::
1381 close_fds() noexcept
1382@@ -41,6 +46,8 @@
1383 p_->close_fds();
1384 }
1385
1386+// LCOV_EXCL_STOP
1387+
1388 void
1389 Daemon::
1390 reset_signals() noexcept
1391
1392=== modified file 'test/gtest/unity/Exceptions_test.cpp'
1393--- test/gtest/unity/Exceptions_test.cpp 2013-06-12 22:07:05 +0000
1394+++ test/gtest/unity/Exceptions_test.cpp 2013-06-17 04:58:28 +0000
1395@@ -17,7 +17,7 @@
1396 */
1397
1398 #include <unity/UnityExceptions.h>
1399-#include <unity/internal/UnityExceptionsImpl.h>
1400+#include <unity/ExceptionImplBase.h>
1401
1402 #include <gtest/gtest.h>
1403
1404@@ -47,6 +47,15 @@
1405 EXPECT_EQ("unity::SyscallException: blah (errno = 0)", e3.to_string(0, " "));
1406 EXPECT_EQ(" unity::SyscallException: blah (errno = 0)", e3.to_string(1, " "));
1407 EXPECT_EQ(" unity::SyscallException: blah (errno = 0)", e3.to_string(2, " "));
1408+
1409+ try
1410+ {
1411+ throw e;
1412+ }
1413+ catch (Exception& e)
1414+ {
1415+ EXPECT_STREQ("unity::SyscallException", e.what());
1416+ }
1417 }
1418
1419 TEST(Exception, empty_reason)
1420@@ -250,19 +259,9 @@
1421 exception_ptr ep = make_exception_ptr(e);
1422
1423 InvalidArgumentException e2("");
1424- exception_ptr ep2 = e2.remember(ep);
1425- EXPECT_EQ(e2.get_earlier(), ep);
1426- }
1427-
1428- {
1429- InvalidArgumentException e("");
1430- exception_ptr ep = make_exception_ptr(e);
1431-
1432- InvalidArgumentException e2("");
1433- exception_ptr ep2 = e2.remember(ep);
1434- EXPECT_EQ(e2.get_earlier(), ep);
1435- }
1436-
1437+ e2.remember(ep);
1438+ EXPECT_EQ(e2.get_earlier(), ep);
1439+ }
1440
1441 // Check that we are following the history chain.
1442
1443@@ -297,7 +296,6 @@
1444 }
1445 }
1446
1447-
1448 // Same test, but this time with nested exceptions in the history.
1449
1450 {
1451@@ -534,3 +532,105 @@
1452 delete ep;
1453 }
1454 }
1455+
1456+class StatelessException : public unity::Exception
1457+{
1458+public:
1459+ explicit StatelessException(std::string const& reason)
1460+ : Exception(make_shared<ExceptionImplBase>(this, reason))
1461+ {
1462+ }
1463+ explicit StatelessException(std::shared_ptr<ExceptionImplBase> const& derived)
1464+ : Exception(derived)
1465+ {
1466+ }
1467+ StatelessException(StatelessException const&) = default;
1468+ StatelessException& operator=(StatelessException const&) = default;
1469+ virtual ~StatelessException() noexcept {}
1470+
1471+ virtual char const* what() const noexcept override
1472+ {
1473+ return "StatelessException";
1474+ }
1475+
1476+ virtual std::exception_ptr self() const override
1477+ {
1478+ return make_exception_ptr(*this);
1479+ }
1480+
1481+};
1482+
1483+TEST(Derivation, stateless_derivation)
1484+{
1485+ StatelessException e("test");
1486+ EXPECT_EQ("test", e.reason());
1487+ EXPECT_STREQ("StatelessException", e.what());
1488+}
1489+
1490+namespace Impl
1491+{
1492+class StatefulExceptionImpl;
1493+}
1494+
1495+class StatefulException : public unity::Exception
1496+{
1497+public:
1498+ StatefulException(std::string const& reason, int state);
1499+ StatefulException(std::shared_ptr<ExceptionImplBase> const& derived)
1500+ : Exception(derived)
1501+ {
1502+ }
1503+ StatefulException(StatefulException const&) = default;
1504+ StatefulException& operator=(StatefulException const&) = default;
1505+ virtual ~StatefulException() noexcept {}
1506+
1507+ virtual char const* what() const noexcept override
1508+ {
1509+ return "StatefulException";
1510+ }
1511+
1512+ virtual std::exception_ptr self() const override
1513+ {
1514+ return make_exception_ptr(*this);
1515+ }
1516+
1517+};
1518+
1519+namespace Impl
1520+{
1521+
1522+class StatefulExceptionImpl : public unity::ExceptionImplBase
1523+{
1524+public:
1525+ StatefulExceptionImpl(StatefulException const* owner, string const& reason, int state)
1526+ : ExceptionImplBase(owner, reason + " state = " + std::to_string(state))
1527+ , state_(state)
1528+ {
1529+ }
1530+ int state_;
1531+};
1532+
1533+}
1534+
1535+StatefulException::
1536+StatefulException(std::string const& reason, int state)
1537+ : Exception(make_shared<Impl::StatefulExceptionImpl>(this, reason, state))
1538+{
1539+}
1540+
1541+TEST(Derivation, stateful_derivation)
1542+{
1543+ StatefulException e("test", 99);
1544+ EXPECT_EQ("test state = 99", e.reason());
1545+ EXPECT_STREQ("StatefulException", e.what());
1546+}
1547+
1548+#if 0
1549+
1550+TEST(Derivation, two_level_derivation)
1551+{
1552+ StatefulException e("test", 99);
1553+ EXPECT_EQ("test state = 99", e.reason());
1554+ EXPECT_STREQ("StatefulException", e.what());
1555+}
1556+#endif

Subscribers

People subscribed via source and target branches

to all changes: