]> git.tdb.fi Git - libs/core.git/blob - source/core/application.cpp
Report uncaught exceptions with a MessageBox on win32
[libs/core.git] / source / core / application.cpp
1 /* $Id$
2
3 This file is part of libmspcore
4 Copyright © 2006 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 /**
23 Constructs an instance of the registered application class and runs it.  If the
24 application throws a UsageError, the static usage() function is called.
25
26 This function can only be called once.  The global main() function provided by
27 the library normally does it automatically at program startup.
28 */
29 int Application::run(int argc, char **argv, void *data)
30 {
31         static bool called=false;
32         if(called)
33         {
34                 cerr<<"Trying to call Application::run_app twice!\n";
35                 return 125;
36         }
37         called=true;
38
39         if(!reg_app_)
40         {
41                 cerr<<"Trying to run with no application class registered!\n";
42                 return 126;
43         }
44
45         data_=data;
46
47         try
48         {
49                 try
50                 {
51                         app_=reg_app_->create_app(argc, argv);
52                 }
53                 catch(const UsageError &e)
54                 {
55                         reg_app_->usage(e.what(), argv[0], e.get_brief());
56                         return 1;
57                 }
58
59                 int result=app_->main();
60                 delete app_;
61                 return result;
62         }
63         catch(const exception &e)
64         {
65                 delete app_;
66
67 #ifdef WIN32
68                 string msg=Debug::demangle(typeid(e).name())+":\n"+e.what();
69                 MessageBox(0, msg.c_str(), "Uncaught exception", MB_OK|MB_ICONERROR);
70 #else
71                 cerr<<"An uncaught exception occurred.\n";
72                 cerr<<"  type:   "<<Debug::demangle(typeid(e).name())<<'\n';
73                 cerr<<"  what(): "<<e.what()<<'\n';
74
75                 const Exception *exc=dynamic_cast<const Exception *>(&e);
76                 if(exc && !exc->get_backtrace().get_frames().empty())
77                 {
78                         cerr<<"  backtrace:\n";
79                         const Debug::Backtrace::FrameSeq &frames=exc->get_backtrace().get_frames();
80                         for(Debug::Backtrace::FrameSeq::const_iterator i=frames.begin(); i!=frames.end(); ++i)
81                         {
82                                 cerr<<"    "<<i->address;
83                                 if(!i->symbol.empty())
84                                         cerr<<" in "<<i->symbol;
85                                 cerr<<" from "<<i->file<<'\n';
86                         }
87                 }
88 #endif
89
90                 return 124;
91         }
92 }
93
94 /**
95 Prints a message describing the usage of the application.  The default version
96 will blame the programmer for being lazy.
97
98 @param   reason  Why the function was called
99 @param   argv0   The value of argv[0], to be used in the message
100 @param   brief   Whether to print a brief or long usage message
101 */
102 void Application::usage(const char *reason, const char *, bool)
103 {
104         if(reason)
105                 cerr<<"UsageError: "<<reason<<'\n';
106         cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n";
107 }
108
109 Application::Application():
110         exit_code(0),
111         loop_mode_(TICK_SLEEP)
112 { }
113
114 /**
115 Default main loop.  Behavior depends on loop_mode_.  A custom main loop should
116 monitor the done member variable and return exit_code.
117 */
118 int Application::main()
119 {
120         if(loop_mode_==NONE)
121                 return 0;
122
123         done=false;
124         while(!done)
125         {
126                 if(loop_mode_==SLEEP)
127                 {
128                         sleep_sem_.wait();
129                         if(!done)
130                                 tick();
131                 }
132                 else if(loop_mode_==TICK_SLEEP)
133                 {
134                         tick();
135                         Time::sleep(Time::msec);
136                 }
137                 else if(loop_mode_==TICK_YIELD)
138                 {
139                         tick();
140 #ifdef WIN32
141                         Sleep(0);
142 #else
143                         sched_yield();
144 #endif
145                 }
146                 else if(loop_mode_==TICK_BUSY)
147                         tick();
148         }
149
150         return exit_code;
151 }
152
153 /**
154 Sets the specified signal to be delivered to the sighandler member function.
155 */
156 void Application::catch_signal(int s)
157 {
158         signal(s, &sighandler_);
159 }
160
161 /**
162 Changes the main loop mode.
163 */
164 void Application::set_loop_mode(LoopMode l)
165 {
166         LoopMode old_mode=loop_mode_;
167         loop_mode_=l;
168         if(old_mode==SLEEP)
169                 sleep_sem_.signal();
170 }
171
172 /**
173 Causes the tick() function to be executed once if loop mode is SLEEP.  Has no
174 effect with other loop modes.
175 */
176 void Application::induce_tick()
177 {
178         if(loop_mode_==SLEEP)
179                 sleep_sem_.signal();
180 }
181
182 /**
183 Causes the application to exit gracefully with the given exit code.
184 */
185 void Application::exit(int c)
186 {
187         done=true;
188         exit_code=c;
189         if(loop_mode_==SLEEP)
190                 sleep_sem_.signal();
191 }
192
193 /**
194 Static wrapper function to call a member function of the Application instance.
195 */
196 void Application::sighandler_(int s)
197 {
198         app_->sighandler(s);
199 }
200
201 Application::RegBase::RegBase()
202 {
203         if(reg_app_)
204         {
205                 cerr<<"Warning: registering the application twice\n";
206                 delete reg_app_;
207         }
208
209         reg_app_=this;
210 }
211
212 Application *Application::app_=0;
213 Application::RegBase *Application::reg_app_=0;
214 void *Application::data_=0;
215
216 } // namespace Msp