1 #ifndef __AUDACITY_EXCEPTION__ 2 #define __AUDACITY_EXCEPTION__ 3 4 /*!******************************************************************** 5 6 Audacity: A Digital Audio Editor 7 8 @file AudacityException.h 9 @brief Declare abstract class AudacityException, some often-used subclasses, and @ref GuardedCall 10 11 Paul Licameli 12 **********************************************************************/ 13 14 #include "MemoryX.h" 15 #include <exception> 16 #include <functional> 17 18 #include "Internat.h" 19 20 //! A type of an exception 21 enum class ExceptionType 22 { 23 Internal, //!< Indicates internal failure from Audacity. 24 BadUserAction, //!< Indicates that the user performed an action that is not allowed. 25 BadEnvironment, //!< Indicates problems with environment, such as a full disk 26 }; 27 28 //! Base class for exceptions specially processed by the application 29 /*! Objects of this type can be thrown and caught in any thread, stored, and then used by the main 30 thread in later idle time to explain the error condition to the user. 31 */ 32 class EXCEPTIONS_API AudacityException /* not final */ 33 { 34 public: AudacityException()35 AudacityException() {} 36 virtual ~AudacityException() = 0; 37 38 //! Action to do in the main thread at idle time of the event loop. 39 virtual void DelayedHandlerAction() = 0; 40 41 static void EnqueueAction( 42 std::exception_ptr pException, 43 std::function<void(AudacityException*)> delayedHandler); 44 45 protected: 46 //! Make this protected to prevent slicing copies 47 AudacityException( const AudacityException& ) = default; 48 49 //! Don't allow moves of this class or subclasses 50 // see https://bugzilla.audacityteam.org/show_bug.cgi?id=2442 51 AudacityException( AudacityException&& ) PROHIBITED; 52 53 //! Disallow assignment 54 AudacityException &operator = ( const AudacityException & ) PROHIBITED; 55 }; 56 57 //! Abstract AudacityException subclass displays a message, specified by further subclass 58 /*! At most one message will be displayed for each pass through the main event idle loop, 59 no matter how many exceptions were caught. */ 60 class EXCEPTIONS_API MessageBoxException /* not final */ 61 : public AudacityException 62 { 63 //! Privatize the inherited function 64 using AudacityException::DelayedHandlerAction; 65 66 //! Do not allow subclasses to change behavior, except by overriding ErrorMessage(). 67 void DelayedHandlerAction() final; 68 69 protected: 70 //! If default-constructed with empty caption, it makes no message box. 71 explicit MessageBoxException( 72 ExceptionType exceptionType, //!< Exception type 73 const TranslatableString &caption //!< Shown in message box's frame; not the actual message 74 ); 75 ~MessageBoxException() override; 76 77 MessageBoxException( const MessageBoxException& ); 78 79 //! %Format the error message for this exception. 80 virtual TranslatableString ErrorMessage() const = 0; ErrorHelpUrl()81 virtual wxString ErrorHelpUrl() const { return helpUrl; }; 82 83 private: 84 TranslatableString caption; //!< Stored caption 85 ExceptionType exceptionType; //!< Exception type 86 87 mutable bool moved { false }; //!< Whether @c *this has been the source of a copy 88 protected: 89 mutable wxString helpUrl{ "" }; 90 }; 91 92 //! A MessageBoxException that shows a given, unvarying string. 93 class EXCEPTIONS_API SimpleMessageBoxException /* not final */ 94 : public MessageBoxException 95 { 96 public: 97 explicit SimpleMessageBoxException( 98 ExceptionType exceptionType, //!< Exception type 99 const TranslatableString &message_, //<! Message to show 100 const TranslatableString &caption = XO("Message"), //<! Short caption in frame around message 101 const wxString &helpUrl_ = "" // Optional URL for help. 102 ) 103 : MessageBoxException { exceptionType, caption } 104 , message{ message_ } 105 { 106 helpUrl = helpUrl_; 107 } 108 ~SimpleMessageBoxException() override; 109 110 SimpleMessageBoxException( const SimpleMessageBoxException& ) = default; 111 SimpleMessageBoxException &operator = ( 112 SimpleMessageBoxException && ) PROHIBITED; 113 114 // Format a default, internationalized error message for this exception. 115 virtual TranslatableString ErrorMessage() const override; 116 117 private: 118 TranslatableString message; //!< Stored message 119 }; 120 121 122 //! A default template parameter for @ref GuardedCall 123 struct DefaultDelayedHandlerAction 124 { operatorDefaultDelayedHandlerAction125 void operator () (AudacityException *pException) const 126 { 127 if ( pException ) 128 pException->DelayedHandlerAction(); 129 } 130 }; 131 132 //! A default template parameter for @ref GuardedCall<R> 133 /*! @tparam R return type from GuardedCall (or convertible to it) */ 134 template <typename R> struct SimpleGuard 135 { SimpleGuardSimpleGuard136 explicit SimpleGuard( 137 R value //!< The value to return from GurdedCall when an exception is handled 138 ) 139 : m_value{ value } {} operatorSimpleGuard140 R operator () ( AudacityException * ) const { return m_value; } 141 const R m_value; 142 }; 143 144 //! Specialization of SimpleGuard, also defining a default value 145 template<> struct SimpleGuard<bool> 146 { 147 explicit SimpleGuard( 148 bool value //!< The value to return from @ref GaurdedCall when an exception is handled 149 ) 150 : m_value{ value } {} 151 bool operator () ( AudacityException * ) const { return m_value; } 152 static SimpleGuard Default() 153 { return SimpleGuard{ false }; } 154 const bool m_value; 155 }; 156 157 //! Specialization of SimpleGuard, also defining a default value 158 template<> struct SimpleGuard<void> 159 { 160 SimpleGuard() {} 161 void operator () ( AudacityException * ) const {} 162 static SimpleGuard Default() { return {}; } 163 }; 164 165 //! Convert a value to a handler function returning that value, suitable for @ref GuardedCall<R> 166 template < typename R > 167 SimpleGuard< R > MakeSimpleGuard( R value ) 168 { return SimpleGuard< R >{ value }; } 169 170 //! Convert a value to a no-op handler function, suitable for @ref GuardedCall<void> 171 inline SimpleGuard< void > MakeSimpleGuard() { return {}; } 172 173 /*! 174 Executes a given function (typically a lamba), in any thread. 175 176 If there is any exception, can invoke another given function as handler, which may rethrow that or 177 another exception, but usually just returns the value for the GuardedCall. 178 179 If AudacityException is handled, then it queues up a delayed handler action for execution later in 180 the event loop at idle time, on the main thread; typically this informs the user of the error. 181 182 The default delayed handler action is simply to invoke a method of the AudacityException, but this 183 too can be specified otherwise by a third function. 184 185 @tparam R Return type, defaulted to void, or else the only explicit template parameter 186 @tparam F1 deduced type of body function; takes no arguments, returns @b R 187 @tparam F2 deduced type of handler function, or defaulted to @ref SimpleGuard<R>; 188 takes pointer to AudacityException, which is null when some other type of exception is caught; 189 return value is converted to @b R 190 @tparam F3 deduced type of delayed handler function, if a nondefault argument is given; 191 takes pointer to AudacityException, return value is unused 192 */ 193 template < 194 typename R = void, 195 196 typename F1, // function object with signature R() 197 198 typename F2 = SimpleGuard< R > // function object 199 // with signature R( AudacityException * ) 200 > 201 //! Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread 202 R GuardedCall( 203 const F1 &body, //!< typically a lambda 204 const F2 &handler = F2::Default(), //!< default just returns false or void; see also @ref MakeSimpleGuard 205 std::function<void(AudacityException*)> delayedHandler 206 = DefaultDelayedHandlerAction{} /*!<called later in the main thread, 207 passing it a stored exception; usually defaulted */ 208 ) 209 { 210 try { return body(); } 211 catch ( AudacityException &e ) { 212 213 auto end = finally([&]{ 214 // At this point, e is the "current" exception, but not "uncaught" 215 // unless it was rethrown by handler. handler might also throw some 216 // other exception object. 217 if (!std::uncaught_exception()) { 218 auto pException = std::current_exception(); // This points to e 219 AudacityException::EnqueueAction( 220 pException, std::move(delayedHandler)); 221 } 222 }); 223 224 return handler( &e ); 225 } 226 catch ( ... ) { 227 return handler( nullptr ); 228 } 229 } 230 231 #endif 232