Consolix
Loading...
Searching...
No Matches
ConsoleApplication.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _CONSOLIX_CONSOLE_APPLICATION_HPP_INCLUDED
3#define _CONSOLIX_CONSOLE_APPLICATION_HPP_INCLUDED
4
8
9#include <csignal>
10
11namespace consolix {
12
19 public:
20
24 static ConsoleApplication* instance = new ConsoleApplication();
25 return *instance;
26 }
27
33 template <typename Component, typename... Args>
34 std::shared_ptr<Component> add(Args&&... args) {
35 return m_manager.add<Component>(std::forward<Args>(args)...);
36 }
37
40 void add(std::shared_ptr<IAppComponent> component) {
41 m_manager.add(std::move(component));
42 }
43
46 void init() {
47 try {
48 while (!m_stopping && !m_manager.initialize()) {
49 std::this_thread::sleep_for(std::chrono::milliseconds(1));
50 }
51 if (m_stopping) {
52 cleanup(0);
53 }
54 } catch (const std::exception& e) {
56 }
57 }
58
62 template <typename InitAction>
63 void init(InitAction init_action) {
64 try {
65 while (!m_stopping && !m_manager.initialize()) {
66 std::this_thread::sleep_for(std::chrono::milliseconds(1));
67 }
68 if (m_stopping) {
69 cleanup(0);
70 }
71 init_action();
72 if (m_stopping) {
73 cleanup(0);
74 }
75 } catch (const std::exception& e) {
77 }
78 }
79
81 void run() {
82 if (m_running) return;
83 m_running = true;
85 init();
87 }
88
92 template <typename IterationAction>
93 void run(IterationAction iteration_action) {
94 if (m_running) return;
95 m_running = true;
97 init();
98 lifecycle_loop(iteration_action);
99 }
100
102 void stop() {
103 if (m_stopping) return;
104 m_stopping = true;
105 }
106
108 void shutdown(int signal) {
109 cleanup(signal);
110 }
111
112 private:
114 std::atomic<bool> m_init{false};
115 std::atomic<bool> m_running{false};
116 std::atomic<bool> m_stopping{false};
117 std::atomic<bool> m_cleanup{false};
118
121# if defined(_WIN32) || defined(_WIN64)
122 SetConsoleCtrlHandler(console_handler, TRUE);
123# else
124 std::signal(SIGINT, signal_handler);
125 std::signal(SIGTERM, signal_handler);
126# endif
127 std::atexit(on_exit_handler);
128 }
129
133 void cleanup(int exit_code, bool wait_for_press = false) {
134 if (m_cleanup) return;
135 m_cleanup = true;
136
137# if CONSOLIX_USE_LOGIT == 1
138 LOGIT_PRINT_INFO("Cleaning up application for exit code: ", exit_code);
139 try {
140 m_manager.shutdown(exit_code);
141 } catch (const std::exception& e) {
142 LOGIT_FATAL(e);
143 }
144 try {
146 } catch (const std::exception& e) {
147 LOGIT_FATAL(e);
148 }
149 LOGIT_SHUTDOWN();
150# else
151 try {
152 m_manager.shutdown(exit_code);
153 } catch (...) {}
154 try {
156 } catch (...) {}
157# endif
158 if (wait_for_press) {
159 CONSOLIX_STREAM() << "Press Enter to exit..." << std::endl;
160 std::cin.get();
161 }
162 std::exit(exit_code);
163 }
164
167 void handle_fatal_exception(const std::exception& e) {
168# if CONSOLIX_USE_LOGIT == 1
169 LOGIT_PRINT_FATAL("Unhandled exception: ", e.what());
170# endif //
171 cleanup(-1, static_cast<bool>(CONSOLIX_WAIT_ON_ERROR));
172 }
173
177 template <typename IterationAction>
178 void lifecycle_loop(IterationAction iteration_action) {
179 try {
180 while (!m_stopping) {
181 m_manager.process();
182 iteration_action();
183 }
184 cleanup(0);
185 } catch (const std::exception& e) {
187 }
188 }
189
192 try {
193 while (!m_stopping) {
194 m_manager.process();
195 }
196 cleanup(0);
197 } catch (const std::exception& e) {
199 }
200 }
201
206
207# if defined(_WIN32) || defined(_WIN64)
208
212 static BOOL WINAPI console_handler(DWORD win_event) {
213 switch (win_event) {
214 case CTRL_C_EVENT:
215 log_event("CTRL_C_EVENT");
216 break;
217 case CTRL_CLOSE_EVENT:
218 log_event("CTRL_CLOSE_EVENT");
219 break;
220 case CTRL_LOGOFF_EVENT:
221 log_event("CTRL_LOGOFF_EVENT");
222 break;
223 case CTRL_SHUTDOWN_EVENT:
224 log_event("CTRL_SHUTDOWN_EVENT");
225 break;
226 default:
227 log_event("UNKNOWN_EVENT");
228 break;
229 }
230 ConsoleApplication::get_instance().shutdown(event_to_exit_code(win_event));
231 return FALSE;
232 }
233
236 static void log_event(const char* event_name) {
237# if CONSOLIX_USE_LOGIT == 1
238 LOGIT_PRINT_INFO("Console event received: ", event_name);
239# endif
240 }
241
245 static int event_to_exit_code(DWORD win_event) {
246 switch (win_event) {
247 case CTRL_C_EVENT:
248 return SIGINT;
249 default:
250 return SIGTERM;
251 };
252 }
253
254# else
255
258 static void signal_handler(int exit_code) {
259 switch (exit_code) {
260 case SIGINT:
261 handle_signal("SIGINT", exit_code);
262 break;
263 case SIGTERM:
264 handle_signal("SIGTERM", exit_code);
265 break;
266 default:
267 handle_signal("UNKNOWN_SIGNAL", exit_code);
268 break;
269 }
270 }
271
275 static void handle_signal(const char* signal_name, int exit_code) {
276# if CONSOLIX_USE_LOGIT == 1
277 LOGIT_PRINT_INFO("POSIX signal received: ", signal_name, ", exit code: ", exit_code);
278# endif
279 cleanup(exit_code);
280 }
281
282# endif
283
286
287 // Deleting copy and move constructors and assignment operators to enforce singleton.
292 }; // ConsoleApplication
293
294}; // namespace consolix
295
296#endif // _CONSOLIX_CONSOLE_APPLICATION_HPP_INCLUDED
#define CONSOLIX_STREAM()
Fallback for general logging.
Manages a collection of application components with lifecycle support.
std::shared_ptr< Component > add(Args &&... args)
Adds a new component to the application.
void add(std::shared_ptr< IAppComponent > component)
Adds an existing component to the application.
ConsoleApplication(const ConsoleApplication &)=delete
void setup_signal_handlers()
Sets up signal handlers for graceful application termination.
ConsoleApplication & operator=(ConsoleApplication &&)=delete
std::atomic< bool > m_running
Flag indicating whether the loop is running.
void lifecycle_loop()
The main lifecycle loop.
void run()
Runs the application with the registered components.
void run(IterationAction iteration_action)
Runs the application with a custom loop action.
static ConsoleApplication & get_instance()
Retrieves the singleton instance of the application.
static void signal_handler(int exit_code)
Handles a POSIX signal and delegates to signal handler.
void lifecycle_loop(IterationAction iteration_action)
The main lifecycle loop with a custom action.
void cleanup(int exit_code, bool wait_for_press=false)
Cleans up the application and shuts down all components.
static void handle_signal(const char *signal_name, int exit_code)
Logs and processes a POSIX signal by initiating application cleanup.
ConsoleApplication & operator=(const ConsoleApplication &)=delete
void init(InitAction init_action)
Initializes the application with a custom action.
ConsoleApplication(ConsoleApplication &&)=delete
static void on_exit_handler()
Called upon normal program termination.
void init()
Initializes the application and its components. This method initializes all components in the manager...
void stop()
Stops the application's main loop.
void handle_fatal_exception(const std::exception &e)
Handles fatal exceptions by logging the error and terminating the application.
static ServiceLocator & get_instance()
Retrieves the singleton instance of the ServiceLocator.
void clear_all()
Clears all registered resources.
#define CONSOLIX_WAIT_ON_ERROR
Enables or disables waiting for user input before exiting on a fatal error.
< Utility modules and helpers.