terminate instead of rethrowing an uncaught exception
[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 #include <signal.h>
8 #include <iostream>
9 #include "../time/units.h"
10 #include "../time/utils.h"
11 #include "application.h"
12 #include "error.h"
13
14 using namespace std;
15
16 namespace Msp {
17
18 /**
19 Constructs an instance of the registered application class and runs it.  If the
20 application throws a UsageError, the static usage() function is called.
21
22 This function can only be called once.  The global main() function provided by
23 the library normally does it automatically at program startup.
24 */
25 int Application::run(int argc, char **argv)
26 {
27         static bool called=false;
28         if(called)
29         {
30                 cerr<<"Trying to call Application::run_app twice!\n";
31                 return 125;
32         }
33         called=true;
34
35         if(!reg_app_)
36         {
37                 cerr<<"Trying to run with no application class registered!\n";
38                 return 126;
39         }
40
41         try
42         {
43                 try
44                 {
45                         app_=reg_app_->create_app(argc, argv);
46                 }
47                 catch(const UsageError &e)
48                 {
49                         reg_app_->usage(e.what(), argv[0], e.get_brief());
50                         return 1;
51                 }
52
53                 int result=app_->main();
54                 delete app_;
55                 return result;
56         }
57         catch(const exception &e)
58         {
59                 cerr<<"An uncaught exception occurred.\n";
60                 cerr<<"  type:   "<<typeid(e).name()<<'\n';
61                 cerr<<"  what(): "<<e.what()<<'\n';
62                 delete app_;
63                 terminate();
64         }
65 }
66
67 /**
68 Prints a message describing the usage of the application.  The default version
69 will blame the programmer for being lazy.
70
71 @param   reason  Why the function was called
72 @param   argv0   The value of argv[0], to be used in the message
73 @param   brief   Whether to print a brief or long usage message
74 */
75 void Application::usage(const char *reason, const char *, bool)
76 {
77         if(reason)
78                 cerr<<"UsageError: "<<reason<<'\n';
79         cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n";
80 }
81
82 Application::Application():
83         exit_code(0),
84         loop_mode_(TICK_SLEEP)
85 { }
86
87 /**
88 Default main loop.  Behavior depends on loop_mode_.  A custom main loop should
89 monitor the done member variable and return exit_code.
90 */
91 int Application::main()
92 {
93         if(loop_mode_==NONE)
94                 return 0;
95
96         done=false;
97         while(!done)
98         {
99                 if(loop_mode_==SLEEP)
100                 {
101                         sleep_sem_.wait();
102                         if(!done)
103                                 tick();
104                 }
105                 else if(loop_mode_==TICK_SLEEP)
106                 {
107                         tick();
108                         Time::sleep(Time::msec);
109                 }
110                 else if(loop_mode_==TICK_YIELD)
111                 {
112                         tick();
113 #ifdef WIN32
114                         Sleep(0);
115 #else
116                         sched_yield();
117 #endif
118                 }
119         }
120
121         return exit_code;
122 }
123
124 /**
125 Sets the specified signal to be delivered to the sighandler member function.
126 */
127 void Application::catch_signal(int s)
128 {
129         signal(s, &sighandler_);
130 }
131
132 /**
133 Changes the main loop mode.
134 */
135 void Application::set_loop_mode(LoopMode l)
136 {
137         LoopMode old_mode=loop_mode_;
138         loop_mode_=l;
139         if(old_mode==SLEEP)
140                 sleep_sem_.signal();
141 }
142
143 /**
144 Causes the tick() function to be executed once if loop mode is SLEEP.  Has no
145 effect with other loop modes.
146 */
147 void Application::induce_tick()
148 {
149         if(loop_mode_==SLEEP)
150                 sleep_sem_.signal();
151 }
152
153 /**
154 Causes the application to exit gracefully with the given exit code.
155 */
156 void Application::exit(int c)
157 {
158         done=true;
159         exit_code=c;
160         if(loop_mode_==SLEEP)
161                 sleep_sem_.signal();
162 }
163
164 /**
165 Static wrapper function to call a member function of the Application instance.
166 */
167 void Application::sighandler_(int s)
168 {
169         app_->sighandler(s);
170 }
171
172 Application::RegBase::RegBase()
173 {
174         if(reg_app_)
175         {
176                 cerr<<"Warning: registering the application twice\n";
177                 delete reg_app_;
178         }
179
180         reg_app_=this;
181 }
182
183 Application *Application::app_=0;
184 Application::RegBase *Application::reg_app_=0;
185
186 } // namespace Msp