1 /*
2  * Copyright 2016-2020 Max Kellermann <max.kellermann@gmail.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #ifndef EXCEPTION_HXX
31 #define EXCEPTION_HXX
32 
33 #include <exception>
34 #include <string>
35 #include <utility>
36 
37 /**
38  * Throws the specified exception.  There is an overload for
39  * std::exception_ptr which throws the contained exception instead of
40  * the std::exception_ptr itself.
41  */
42 template<typename T>
43 [[noreturn]]
44 inline void
ThrowException(T && t)45 ThrowException(T &&t)
46 {
47 	throw std::forward<T>(t);
48 }
49 
50 [[noreturn]]
51 inline void
ThrowException(std::exception_ptr ep)52 ThrowException(std::exception_ptr ep)
53 {
54 	std::rethrow_exception(ep);
55 }
56 
57 /**
58  * Create a nested exception, wrapping #ep inside the
59  * std::current_exception().
60  */
61 template<typename T>
62 inline std::exception_ptr
NestCurrentException(T && t)63 NestCurrentException(T &&t) noexcept
64 {
65 	try {
66 		std::throw_with_nested(std::forward<T>(t));
67 	} catch (...) {
68 		return std::current_exception();
69 	}
70 }
71 
72 /**
73  * Create a nested exception, wrapping #ep inside (a copy of) #t.
74  */
75 template<typename T>
76 inline std::exception_ptr
NestException(std::exception_ptr ep,T && t)77 NestException(std::exception_ptr ep, T &&t) noexcept
78 {
79 	try {
80 		std::rethrow_exception(ep);
81 	} catch (...) {
82 		return NestCurrentException(std::forward<T>(t));
83 	}
84 }
85 
86 /**
87  * Find an instance of #T in the nested exception chain, and rethrow
88  * it.  Does nothing of no such instance was found.
89  */
90 template<typename T>
91 inline void
FindRetrowNested(std::exception_ptr ep)92 FindRetrowNested(std::exception_ptr ep)
93 {
94 	try {
95 		std::rethrow_exception(ep);
96 	} catch (const T &t) {
97 		throw;
98 	} catch (const std::exception &e) {
99 		try {
100 			std::rethrow_if_nested(e);
101 		} catch (...) {
102 			FindRetrowNested<T>(std::current_exception());
103 		}
104 	} catch (const std::nested_exception &ne) {
105 		FindRetrowNested<T>(ne.nested_ptr());
106 	} catch (...) {
107 	}
108 }
109 
110 /**
111  * Obtain the full concatenated message of an exception and its nested
112  * chain.
113  */
114 std::string
115 GetFullMessage(const std::exception &e,
116 	       const char *fallback="Unknown exception",
117 	       const char *separator="; ") noexcept;
118 
119 /**
120  * Obtain the full concatenated message of an exception and its nested
121  * chain.
122  */
123 std::string
124 GetFullMessage(std::exception_ptr ep,
125 	       const char *fallback="Unknown exception",
126 	       const char *separator="; ") noexcept;
127 
128 #endif
129