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