]> git.tdb.fi Git - libs/core.git/blob - source/core/application.cpp
Prevent a crash if application destructor throws
[libs/core.git] / source / core / application.cpp
1 /* $Id$
2
3 This file is part of libmspcore
4 Copyright © 2006-2008, 2011  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <signal.h>
9 #include <iostream>
10 #include <typeinfo>
11 #include "../debug/backtrace.h"
12 #include "../debug/demangle.h"
13 #include "../time/units.h"
14 #include "../time/utils.h"
15 #include "application.h"
16 #include "except.h"
17
18 using namespace std;
19
20 namespace Msp {
21
22 Application::Application():
23         exit_code(0)
24 { }
25
26 /**
27 Constructs an instance of the registered application class and runs it.  If the
28 application throws a UsageError, the static usage() function is called.
29
30 This function can only be called once.  The global main() function provided by
31 the library normally does it automatically at program startup.
32 */
33 int Application::run(int argc, char **argv, void *data)
34 {
35         static bool called=false;
36         if(called)
37         {
38                 cerr<<"Trying to call Application::run_app twice!\n";
39                 return 125;
40         }
41         called=true;
42
43         if(!starter_)
44         {
45                 cerr<<"Trying to run with no RegisteredApplication class!\n";
46                 return 126;
47         }
48
49         data_=data;
50
51         try
52         {
53                 try
54                 {
55                         app_=starter_->create_app(argc, argv);
56                 }
57                 catch(const UsageError &e)
58                 {
59                         starter_->usage(e.what(), argv[0], e.get_brief());
60                         return 1;
61                 }
62
63                 int result=app_->main();
64                 Application *a=app_;
65                 app_=0;
66                 delete a;
67                 return result;
68         }
69         catch(const exception &e)
70         {
71                 delete app_;
72
73 #ifdef WIN32
74                 string msg=Debug::demangle(typeid(e).name())+":\n"+e.what();
75                 MessageBoxA(0, msg.c_str(), "Uncaught exception", MB_OK|MB_ICONERROR);
76 #else
77                 cerr<<"An uncaught exception occurred.\n";
78                 cerr<<"  type:   "<<Debug::demangle(typeid(e).name())<<'\n';
79                 cerr<<"  what(): "<<e.what()<<'\n';
80
81                 const Exception *exc=dynamic_cast<const Exception *>(&e);
82                 if(exc && !exc->get_backtrace().get_frames().empty())
83                 {
84                         cerr<<"  backtrace:\n";
85                         const list<Debug::Backtrace::StackFrame> &frames=exc->get_backtrace().get_frames();
86                         for(list<Debug::Backtrace::StackFrame>::const_iterator i=frames.begin(); i!=frames.end(); ++i)
87                                 cerr<<"    "<<*i<<'\n';
88                 }
89 #endif
90
91                 return 124;
92         }
93 }
94
95 /**
96 Prints a message describing the usage of the application.  The default version
97 will blame the programmer for being lazy.
98
99 @param   reason  Why the function was called
100 @param   argv0   The value of argv[0], to be used in the message
101 @param   brief   Whether to print a brief or long usage message
102 */
103 void Application::usage(const char *reason, const char *, bool)
104 {
105         if(reason)
106                 cerr<<"UsageError: "<<reason<<'\n';
107         cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n";
108 }
109
110 /**
111 Default main loop.  Calls tick() repeatedly until exit() is called.  A custom
112 main loop should monitor the done member variable and return exit_code.
113 */
114 int Application::main()
115 {
116         done = false;
117         while(!done)
118                 tick();
119
120         return exit_code;
121 }
122
123 /**
124 Sets the specified signal to be delivered to the sighandler member function.
125 */
126 void Application::catch_signal(int s)
127 {
128         signal(s, &sighandler_);
129 }
130
131 /**
132 Causes the application to exit gracefully with the given exit code.
133 */
134 void Application::exit(int c)
135 {
136         done = true;
137         exit_code = c;
138 }
139
140 /**
141 Static wrapper function to call a member function of the Application instance.
142 */
143 void Application::sighandler_(int s)
144 {
145         app_->sighandler(s);
146 }
147
148
149 Application::Starter::Starter()
150 {
151         if(starter_)
152                 throw InvalidState("Can't create more than one Starter instance");
153
154         starter_=this;
155 }
156
157 Application *Application::app_=0;
158 Application::RegBase *Application::reg_app_=0;
159 void *Application::data_=0;
160
161 } // namespace Msp