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