mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-16 16:10:06 +02:00
Exception framework: define AudacityException and GuardedCall...
AudacityException is an abstract base class for exceptions generated by Audacity. GuardedCall wraps any function (usually a lambda) in an appropriate catch block. It can also accept a second function that defines a catch block action, which can rethrow or return a value for the GuardedCall. It can also accept a third function, that defines another, delayed action that executes in the main thread at idle time if the second function intercepts an AudacityException and completes without rethrow. Defaults for the second function simply return void or false. Default for the third function invokes a virtual method of AudacityException, which for subclass MessageBoxException, displays a message box.
This commit is contained in:
parent
26f917b1fd
commit
df077d171a
@ -1209,6 +1209,7 @@
|
||||
5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */; };
|
||||
5E74D2E41CC4429700D88B0B /* PlayIndicatorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */; };
|
||||
5E74D2E51CC4429700D88B0B /* Scrubbing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */; };
|
||||
5E78388E1DE4995F003270C0 /* AudacityException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E78388D1DE4995E003270C0 /* AudacityException.cpp */; };
|
||||
5E79B3411D5CC38D001D677D /* ImportGStreamer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E79B33F1D5CC38D001D677D /* ImportGStreamer.cpp */; };
|
||||
5E94A1BA1D1F1C8400A8713A /* wxPanelWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E94A1B81D1F1C8400A8713A /* wxPanelWrapper.cpp */; };
|
||||
5ED1D0AD1CDE55BD00471E3C /* Overlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5ED1D0A91CDE55BD00471E3C /* Overlay.cpp */; };
|
||||
@ -2988,12 +2989,14 @@
|
||||
5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayIndicatorOverlay.h; sourceTree = "<group>"; };
|
||||
5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scrubbing.cpp; sourceTree = "<group>"; };
|
||||
5E74D2E21CC4429700D88B0B /* Scrubbing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scrubbing.h; sourceTree = "<group>"; };
|
||||
5E78388D1DE4995E003270C0 /* AudacityException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudacityException.cpp; sourceTree = "<group>"; };
|
||||
5E7838931DE4BBC2003270C0 /* CommandFlag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommandFlag.h; sourceTree = "<group>"; };
|
||||
5E79B33F1D5CC38D001D677D /* ImportGStreamer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImportGStreamer.cpp; sourceTree = "<group>"; };
|
||||
5E79B3401D5CC38D001D677D /* ImportGStreamer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImportGStreamer.h; sourceTree = "<group>"; };
|
||||
5E94A1B81D1F1C8400A8713A /* wxPanelWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wxPanelWrapper.cpp; sourceTree = "<group>"; };
|
||||
5E94A1B91D1F1C8400A8713A /* wxPanelWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wxPanelWrapper.h; sourceTree = "<group>"; };
|
||||
5EB9EA281D5B81270050AF40 /* ImportForwards.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImportForwards.h; sourceTree = "<group>"; };
|
||||
5ECCE7651DE49834009900E9 /* AudacityException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudacityException.h; sourceTree = "<group>"; };
|
||||
5ED18DB61CC16B1E00FAFE95 /* Reverb_libSoX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reverb_libSoX.h; sourceTree = "<group>"; };
|
||||
5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wxFileNameWrapper.h; sourceTree = "<group>"; };
|
||||
5ED1D0A91CDE55BD00471E3C /* Overlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Overlay.cpp; sourceTree = "<group>"; };
|
||||
@ -3833,6 +3836,7 @@
|
||||
1790AFC709883BFD008A330A /* AboutDialog.cpp */,
|
||||
1790AFC909883BFD008A330A /* AColor.cpp */,
|
||||
1790AFCE09883BFD008A330A /* AudacityApp.cpp */,
|
||||
5E78388D1DE4995E003270C0 /* AudacityException.cpp */,
|
||||
28C3946B1818356800FDDAC9 /* AudacityLogger.cpp */,
|
||||
1790AFD209883BFD008A330A /* AudioIO.cpp */,
|
||||
28560C8F0A75E40F00A3429E /* AutoRecovery.cpp */,
|
||||
@ -3917,6 +3921,7 @@
|
||||
28FB12230A3790DF006F0917 /* AllThemeResources.h */,
|
||||
1790AFCC09883BFD008A330A /* Audacity.h */,
|
||||
1790AFCF09883BFD008A330A /* AudacityApp.h */,
|
||||
5ECCE7651DE49834009900E9 /* AudacityException.h */,
|
||||
1790AFD009883BFD008A330A /* AudacityHeaders.h */,
|
||||
28C3946C1818356800FDDAC9 /* AudacityLogger.h */,
|
||||
1790AFD309883BFD008A330A /* AudioIO.h */,
|
||||
@ -7529,6 +7534,7 @@
|
||||
284B279C0FC66864005EAC96 /* LibraryPrefs.cpp in Sources */,
|
||||
284B279D0FC66864005EAC96 /* PlaybackPrefs.cpp in Sources */,
|
||||
284B279E0FC66864005EAC96 /* ProjectsPrefs.cpp in Sources */,
|
||||
5E78388E1DE4995F003270C0 /* AudacityException.cpp in Sources */,
|
||||
284B279F0FC66864005EAC96 /* RecordingPrefs.cpp in Sources */,
|
||||
284B27E40FC66CCD005EAC96 /* TracksPrefs.cpp in Sources */,
|
||||
284B27E50FC66CCD005EAC96 /* WarningsPrefs.cpp in Sources */,
|
||||
|
69
src/AudacityException.cpp
Normal file
69
src/AudacityException.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "Audacity.h"
|
||||
#include "AudacityException.h"
|
||||
|
||||
#include <wx/atomic.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
AudacityException::~AudacityException()
|
||||
{
|
||||
}
|
||||
|
||||
wxAtomicInt sOutstandingMessages {};
|
||||
|
||||
MessageBoxException::MessageBoxException( const wxString &caption_ )
|
||||
: caption{ caption_ }
|
||||
{
|
||||
wxAtomicInc( sOutstandingMessages );
|
||||
}
|
||||
|
||||
// The class needs a copy constructor to be throwable
|
||||
// (or will need it, by C++14 rules). But the copy
|
||||
// needs to act like a move constructor. There must be unique responsibility
|
||||
// for each exception thrown to decrease the global count when it is handled.
|
||||
MessageBoxException::MessageBoxException( const MessageBoxException& that )
|
||||
{
|
||||
caption = that.caption;
|
||||
moved = that.moved;
|
||||
that.moved = true;
|
||||
}
|
||||
|
||||
MessageBoxException &MessageBoxException::operator = ( MessageBoxException &&that )
|
||||
{
|
||||
caption = that.caption;
|
||||
if ( this != &that ) {
|
||||
if (!moved)
|
||||
wxAtomicDec( sOutstandingMessages );
|
||||
|
||||
moved = that.moved;
|
||||
that.moved = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
MessageBoxException::~MessageBoxException()
|
||||
{
|
||||
if (!moved)
|
||||
// If exceptions are used properly, you should never reach this,
|
||||
// because moved should become true earlier in the object's lifetime.
|
||||
wxAtomicDec( sOutstandingMessages );
|
||||
}
|
||||
|
||||
// This is meant to be invoked via wxEvtHandler::CallAfter
|
||||
void MessageBoxException::DelayedHandlerAction()
|
||||
{
|
||||
if (!moved) {
|
||||
// This test prevents accumulation of multiple messages between idle
|
||||
// times of the main even loop. Only the last queued exception
|
||||
// displays its message. We assume that multiple messages have a
|
||||
// common cause such as exhaustion of disk space so that the others
|
||||
// give the user no useful added information.
|
||||
if ( wxAtomicDec( sOutstandingMessages ) == 0 )
|
||||
::wxMessageBox(
|
||||
ErrorMessage(),
|
||||
caption.IsEmpty() ? wxMessageBoxCaptionStr : caption,
|
||||
wxICON_ERROR
|
||||
);
|
||||
moved = true;
|
||||
}
|
||||
}
|
181
src/AudacityException.h
Normal file
181
src/AudacityException.h
Normal file
@ -0,0 +1,181 @@
|
||||
#ifndef __AUDACITY_EXCEPTION__
|
||||
#define __AUDACITY_EXCEPTION__
|
||||
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
AudacityException.h
|
||||
|
||||
Paul Licameli
|
||||
|
||||
Define the root of a hierarchy of classes that are thrown and caught
|
||||
by Audacity.
|
||||
|
||||
Define some subclasses. Not all subclasses need be defined here.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "MemoryX.h"
|
||||
#include <wx/app.h>
|
||||
|
||||
class wxString;
|
||||
|
||||
class AudacityException /* not final */
|
||||
{
|
||||
public:
|
||||
AudacityException() {}
|
||||
virtual ~AudacityException() = 0;
|
||||
|
||||
// This is intended as a "polymorphic move copy constructor"
|
||||
// which leaves this "empty".
|
||||
// We would not need this if we had std::exception_ptr
|
||||
virtual std::unique_ptr< AudacityException > Move() = 0;
|
||||
|
||||
// Action to do in the main thread at idle time of the event loop.
|
||||
virtual void DelayedHandlerAction() = 0;
|
||||
|
||||
protected:
|
||||
// Make this protected to prevent slicing copies
|
||||
AudacityException( AudacityException&& ) {}
|
||||
AudacityException( const AudacityException& ) = default;
|
||||
AudacityException &operator = (AudacityException &&) {}
|
||||
AudacityException &operator = ( const AudacityException & ) PROHIBITED;
|
||||
};
|
||||
|
||||
// A subclass of AudacityException whose delayed handler action displays
|
||||
// a message box. The message is specified by further subclasses.
|
||||
// Not more than one message box will be displayed for each pass through
|
||||
// the main event idle loop.
|
||||
class MessageBoxException /* not final */ : public AudacityException
|
||||
{
|
||||
// Do not allow subclasses to change this behavior further, except
|
||||
// by overriding ErrorMessage()
|
||||
using AudacityException::DelayedHandlerAction;
|
||||
void DelayedHandlerAction() override;
|
||||
|
||||
protected:
|
||||
explicit MessageBoxException( const wxString &caption = wxString{} );
|
||||
~MessageBoxException() override;
|
||||
|
||||
MessageBoxException( const MessageBoxException& );
|
||||
MessageBoxException &operator = ( MessageBoxException && );
|
||||
|
||||
// Format a default, internationalized error message for this exception.
|
||||
virtual wxString ErrorMessage() const = 0;
|
||||
|
||||
private:
|
||||
wxString caption;
|
||||
mutable bool moved { false };
|
||||
};
|
||||
|
||||
struct DefaultDelayedHandlerAction
|
||||
{
|
||||
void operator () (AudacityException *pException) const
|
||||
{
|
||||
if ( pException )
|
||||
pException->DelayedHandlerAction();
|
||||
}
|
||||
};
|
||||
|
||||
// Helpers for defining GuardedCall:
|
||||
|
||||
// Call one function object,
|
||||
// then another unless the first throws, return result of first
|
||||
template <typename R> struct Sequencer {
|
||||
template <typename F1, typename Argument, typename F2>
|
||||
R operator () (const F1 &f1, Argument &&a, const F2 &f2)
|
||||
{
|
||||
auto result = f1( std::forward<Argument>(a) );
|
||||
f2();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
// template specialization to allow R to be void
|
||||
template <> struct Sequencer<void> {
|
||||
template <typename F1, typename Argument, typename F2>
|
||||
void operator () (const F1 &f1, Argument &&a, const F2 &f2)
|
||||
{
|
||||
f1( std::forward<Argument>(a) );
|
||||
f2();
|
||||
}
|
||||
};
|
||||
|
||||
// Classes that can supply the second argument of GuardedCall:
|
||||
// Frequently useful converter of all exceptions to some failure constant
|
||||
template <typename R> struct SimpleGuard
|
||||
{
|
||||
explicit SimpleGuard( R value ) : m_value{ value } {}
|
||||
R operator () ( AudacityException * ) const { return m_value; }
|
||||
const R m_value;
|
||||
};
|
||||
|
||||
// Simple guard specialization that returns bool, and defines Default
|
||||
template<> struct SimpleGuard<bool>
|
||||
{
|
||||
explicit SimpleGuard( bool value ) : m_value{ value } {}
|
||||
bool operator () ( AudacityException * ) const { return m_value; }
|
||||
static SimpleGuard Default()
|
||||
{ return SimpleGuard{ false }; }
|
||||
const bool m_value;
|
||||
};
|
||||
|
||||
// Simple guard specialization that returns nothing, and defines Default
|
||||
template<> struct SimpleGuard<void>
|
||||
{
|
||||
SimpleGuard() {}
|
||||
void operator () ( AudacityException * ) const {}
|
||||
static SimpleGuard Default() { return {}; }
|
||||
};
|
||||
|
||||
template < typename R >
|
||||
SimpleGuard< R > MakeSimpleGuard( R value )
|
||||
{ return SimpleGuard< R >{ value }; }
|
||||
|
||||
inline SimpleGuard< void > MakeSimpleGuard() { return {}; }
|
||||
|
||||
/**
|
||||
* Call the body function (usually a lambda) inside a try block.
|
||||
*
|
||||
* The handler intercepts exceptions, and is passed nullptr if the
|
||||
* exception is of a type not defined by Audacity. It may return a value
|
||||
* for the guarded call or throw the same or another exception.
|
||||
* It executes in the same thread as the body.
|
||||
*
|
||||
* If the handler catches non-null and does not throw, then delayedHandler
|
||||
* executes later in the main thread, in idle time of the event loop.
|
||||
*/
|
||||
template <
|
||||
typename R, // return type
|
||||
|
||||
typename F1, // function object with signature R()
|
||||
|
||||
typename F2 = SimpleGuard< R >, // function object
|
||||
// with signature R( AudacityException * )
|
||||
|
||||
typename F3 =
|
||||
DefaultDelayedHandlerAction // Any( AudacityException * ), ignore return
|
||||
>
|
||||
R GuardedCall
|
||||
( const F1 &body,
|
||||
const F2 &handler = F2::Default(),
|
||||
const F3 &delayedHandler = {} )
|
||||
{
|
||||
try { return body(); }
|
||||
catch ( AudacityException &e ) {
|
||||
return Sequencer<R>{}( handler, &e,
|
||||
[&] {
|
||||
auto pException =
|
||||
std::shared_ptr< AudacityException > { e.Move().release() };
|
||||
wxTheApp->CallAfter( [=] { // capture pException by value
|
||||
delayedHandler( pException.get() );
|
||||
} );
|
||||
}
|
||||
);
|
||||
}
|
||||
catch ( ... ) {
|
||||
return handler( nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -101,6 +101,8 @@ audacity_SOURCES = \
|
||||
Audacity.h \
|
||||
AudacityApp.cpp \
|
||||
AudacityApp.h \
|
||||
AudacityException.cpp \
|
||||
AudacityException.h \
|
||||
AudacityLogger.cpp \
|
||||
AudacityLogger.h \
|
||||
AudioIO.cpp \
|
||||
|
@ -121,6 +121,7 @@
|
||||
<ClCompile Include="..\..\..\src\AboutDialog.cpp" />
|
||||
<ClCompile Include="..\..\..\src\AColor.cpp" />
|
||||
<ClCompile Include="..\..\..\src\AudacityApp.cpp" />
|
||||
<ClCompile Include="..\..\..\src\AudacityException.cpp" />
|
||||
<ClCompile Include="..\..\..\src\AudacityHeaders.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
@ -411,6 +412,7 @@
|
||||
<ClInclude Include="..\..\..\src\AllThemeResources.h" />
|
||||
<ClInclude Include="..\..\..\src\Audacity.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityApp.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityException.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityHeaders.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityLogger.h" />
|
||||
<ClInclude Include="..\..\..\src\AudioIO.h" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="src">
|
||||
@ -887,6 +887,9 @@
|
||||
<ClCompile Include="..\..\..\src\widgets\wxPanelWrapper.cpp">
|
||||
<Filter>src\widgets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\AudacityException.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\src\AboutDialog.h">
|
||||
@ -1801,6 +1804,9 @@
|
||||
<ClInclude Include="..\..\..\src\commands\CommandFlag.h">
|
||||
<Filter>src\commands</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\AudacityException.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="..\..\audacity.ico">
|
||||
@ -2024,4 +2030,4 @@
|
||||
<Filter>plug-ins</Filter>
|
||||
</copy>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user