pure-cpp 1.0.0
A C++ physics simulation benchmark comparing performance with Python implementations
app_manager.cpp
Go to the documentation of this file.
1/**
2 * \file app_manager.cpp
3 * \brief Implementation of the application lifecycle manager.
4 * \author Le Bars, Yoann
5 *
6 * This file is part of the pure C++ benchmark.
7 */
8
9#include "app_manager.hpp"
10
11#include <QAction>
12#include <QMainWindow>
13#include <QMenu>
14#include <QMenuBar>
15#include <QWidget>
16#include <chrono>
17
18#include "app_profiler.hpp"
19
20namespace App {
21
22 /**
23 * \brief Constructs the application manager and sets application-wide
24 * metadata.
25 */
26 AppManager::AppManager() : mainWindow_(nullptr) {
27 QApplication::setApplicationName(Configuration::PROJECT_NAME);
28 QApplication::setOrganizationName("eiG");
29 const QString version = QStringLiteral("%1.%2.%3")
30 .arg(Configuration::VERSION_MAJOR)
31 .arg(Configuration::VERSION_MINOR)
32 .arg(Configuration::PATCH_VERSION);
33 QApplication::setApplicationVersion(version);
34 }
35
36 /**
37 * \brief Sets up all application components before running.
38 */
40 /* Pre-scan for the diagnostics flag. This is done before full argument
41 parsing to enable debug output during translation loading, which
42 happens before the logger is fully configured. */
43 bool diagnosticsEnabled = false;
44 for (const auto& arg : QApplication::arguments()) {
45 if (arg == QLatin1String("-D") ||
46 arg == QLatin1String("--diagnostics") ||
47 arg == QLatin1String("-c") ||
48 arg == QLatin1String("--debugConsole")) {
49 diagnosticsEnabled = true;
50 break;
51 }
52 }
54
55 // Create a lambda for translation to pass to the command-line parser.
56 // Use "CmdLine::CmdLineArgs" context to match the existing translations
57 // in the .ts files.
58 auto tr_func = [](const char* text) {
59 return QCoreApplication::translate("CmdLine::CmdLineArgs", text);
60 };
61 args_ = CmdLine::CmdLineArgs::parse(QApplication::arguments(), tr_func);
62 if (!args_) {
63 return false; // --help or --version was requested.
64 }
65
66 logger_ = std::make_unique<AppUtils::Logger>(*args_);
67 return true;
68 }
69
70 /**
71 * \brief Runs the main application event loop.
72 */
74 // Create the QML bridge (used for Display and actions)
76 bridge_ = new Window::QmlBridge(args_->toSimulationConfig(), this);
78
79 // Create a main window widget to host the Display
80 mainWindow_ = new QMainWindow();
81 mainWindow_->setWindowTitle(
82 QApplication::translate("MainWindow", "Pure C++"));
83 mainWindow_->resize(800, 600);
84
85 // Create native menu bar with QML bridge integration
86 // Use "MainWindow" context to match the translations from main.ui
87 QMenuBar* menuBar = mainWindow_->menuBar();
88
89 // File menu
90 QMenu* fileMenu =
91 menuBar->addMenu(QApplication::translate("MainWindow", "&File"));
92 QAction* quitAction =
93 fileMenu->addAction(QApplication::translate("MainWindow", "&Quit"));
94 quitAction->setShortcut(QKeySequence::Quit);
95 connect(quitAction, &QAction::triggered, bridge_,
97
98 // Help menu
99 QMenu* helpMenu =
100 menuBar->addMenu(QApplication::translate("MainWindow", "&Help"));
101 QAction* aboutAction = helpMenu->addAction(
102 QApplication::translate("MainWindow", "About Pure C++"));
103 connect(aboutAction, &QAction::triggered, bridge_,
105 QAction* aboutQtAction = helpMenu->addAction(
106 QApplication::translate("MainWindow", "About Qt"));
107 connect(aboutQtAction, &QAction::triggered, bridge_,
109
110 // Add the Display container as the central widget
111 // The container is owned by mainWindow, so it will be destroyed when
112 // mainWindow is destroyed
113 if (bridge_->displayContainer()) {
114 mainWindow_->setCentralWidget(bridge_->displayContainer());
115 }
116
117 // Connect the application's aboutToQuit signal to our cleanup slot.
118 // This ensures cleanup happens before the application exits
119 connect(qApp, &QApplication::aboutToQuit, this, &AppManager::cleanup);
120
121 // Connect simulation finished to close the window
123 &QMainWindow::close, Qt::QueuedConnection);
124
125 // Prevent automatic deletion on close - let QApplication handle
126 // destruction
127 mainWindow_->setAttribute(Qt::WA_DeleteOnClose, false);
128
129 // Show the window
130 mainWindow_->show();
131
132 // Start the simulation
133 bridge_->startSimulation();
134
135 // Start timing the event loop
137 int exitCode = QApplication::exec();
138 // Stop timing the event loop (exec() has returned)
140 return exitCode;
141 }
142
143 /**
144 * \brief Performs cleanup after the event loop has finished.
145 *
146 * Two modes are supported via --cleanup-mode:
147 * - fast (default): physics shutdown is synchronous; Qt3D/UI teardown is
148 * deferred (deleteLater), comparable to async Python GC for benchmarks.
149 * - strict: synchronous UI teardown and processEvents for Valgrind.
150 */
152 using CleanupClock = std::chrono::high_resolution_clock;
153 auto phaseStart = CleanupClock::now();
154
155 // Stop event loop timing if it's still running (should already be
156 // stopped, but be safe)
159
160 const bool useFastCleanup =
161 !args_ || args_->cleanup_mode_ != "strict";
162
163 QWidget* container = nullptr;
164 if (mainWindow_ && bridge_) {
165 container = bridge_->displayContainer();
166 if (container) {
167 phaseStart = CleanupClock::now();
168 mainWindow_->setCentralWidget(nullptr);
170 std::chrono::duration<double>(CleanupClock::now() -
171 phaseStart)
172 .count());
173 }
174 }
175
176 // Always stop the physics thread before QApplication is destroyed.
177 phaseStart = CleanupClock::now();
178 if (bridge_) {
179 bridge_->shutdownSimulation();
180 }
182 std::chrono::duration<double>(CleanupClock::now() - phaseStart)
183 .count());
184
185 if (container && bridge_) {
186 const auto manualDestroyStart = CleanupClock::now();
187 phaseStart = CleanupClock::now();
188 if (useFastCleanup) {
189 bridge_->releaseDisplayDeferred(container);
190 } else {
191 bridge_->releaseDisplayStrict(container);
192 }
194 std::chrono::duration<double>(CleanupClock::now() - phaseStart)
195 .count());
197 std::chrono::duration<double>(CleanupClock::now() - phaseStart)
198 .count());
200 std::chrono::duration<double>(CleanupClock::now() -
201 manualDestroyStart)
202 .count());
203 }
204
205 if (mainWindow_) {
206 phaseStart = CleanupClock::now();
207 if (useFastCleanup) {
208 mainWindow_->deleteLater();
209 } else {
210 delete mainWindow_;
211 }
212 mainWindow_ = nullptr;
213
214 if (!useFastCleanup) {
215 phaseStart = CleanupClock::now();
216 QApplication::processEvents(
217 QEventLoop::ExcludeUserInputEvents);
219 std::chrono::duration<double>(CleanupClock::now() -
220 phaseStart)
221 .count());
222 }
223 }
224
227 }
228
229} // namespace App
Global application profiling instrumentation.
QPointer< Window::QmlBridge > bridge_
The QML bridge for exposing C++ functionality.
Definition: app_manager.hpp:57
int run()
Runs the main application event loop.
Definition: app_manager.cpp:73
bool setup()
Sets up all application components before running.
Definition: app_manager.cpp:39
QMainWindow * mainWindow_
The main window (stored for proper cleanup order).
Definition: app_manager.hpp:59
void cleanup()
Performs cleanup after the event loop has finished.
std::unique_ptr< AppUtils::Logger > logger_
The logger for the application.
Definition: app_manager.hpp:53
AppManager()
Constructs the application manager and sets application-wide metadata.
Definition: app_manager.cpp:26
std::unique_ptr< CmdLine::CmdLineArgs > args_
The command line arguments for the application.
Definition: app_manager.hpp:55
static void addCleanupAppProcessEvents(double seconds)
Adds measured time spent in AppManager cleanup processEvents.
static void addCleanupAppManualDestroy(double seconds)
Adds measured time for manual destroy operations.
static void startEventLoop()
Starts timing the Qt event loop.
static void printReport()
Prints the profiling report.
static void addCleanupAppBridgeCleanup(double seconds)
Adds measured time spent in bridge/display cleanup call.
static void addCleanupAppDetachContainer(double seconds)
Adds measured time for detaching the display container.
static void stopEventLoop()
Stops timing the Qt event loop.
static void startCleanup()
Starts timing cleanup.
static void addCleanupAppContainerDeleteSchedule(double seconds)
Adds measured time for container delete action.
static void stopWindowCreation()
Stops timing 3D window creation.
static void addCleanupAppReleaseDisplay(double seconds)
Adds measured time for explicit display release.
static void startWindowCreation()
Starts timing 3D window creation.
static void stopCleanup()
Stops timing cleanup.
static void loadTranslations(bool in_diagnosticsEnabled)
Loads and installs the best-matching translation for the system’s locale.
Bridge class to expose C++ functionality to QML.
Definition: qml_bridge.hpp:44
void showAboutQt()
Slot to display the "About Qt" dialogue.
Definition: qml_bridge.cpp:135
void quit()
Slot to quit the application.
Definition: qml_bridge.cpp:137
void simulationFinished()
Emitted when the simulation finishes.
void showAbout()
Slot to display the "About" dialogue.
Definition: qml_bridge.cpp:129
static std::unique_ptr< CmdLineArgs > parse(const QStringList &q_args, const std::function< QString(const char *)> &tr_func)
Parses and validates command-line arguments.