Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
dynamic_library.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <expected>
5#include <filesystem>
6#include <string>
7#include <string_view>
8#include <type_traits>
9
10namespace helios::utils {
11
12/**
13 * @brief Error codes for dynamic library operations.
14 */
15enum class DynamicLibraryError : uint8_t {
16 FileNotFound, ///< Library file not found
17 LoadFailed, ///< Failed to load library
18 SymbolNotFound, ///< Symbol not found in library
19 InvalidHandle, ///< Invalid library handle
20 AlreadyLoaded, ///< Library is already loaded
21 NotLoaded, ///< Library is not loaded
22 PlatformError, ///< Platform-specific error
23};
24
25/**
26 * @brief Gets a human-readable description for a DynamicLibraryError.
27 * @param error The error code
28 * @return String description of the error
29 */
30[[nodiscard]] constexpr std::string_view DynamicLibraryErrorToString(
31 DynamicLibraryError error) noexcept {
32 switch (error) {
34 return "Library file not found";
36 return "Failed to load library";
38 return "Symbol not found in library";
40 return "Invalid library handle";
42 return "Library is already loaded";
44 return "Library is not loaded";
46 return "Platform-specific error";
47 default:
48 return "Unknown error";
49 }
50}
51
52/**
53 * @brief Cross-platform dynamic library loader.
54 * @details Provides a unified interface for loading dynamic libraries (DLLs on
55 * Windows, .so on Linux, .dylib on macOS) and retrieving function symbols.
56 *
57 * @note Not thread-safe. External synchronization required for concurrent
58 * access.
59 *
60 * @code
61 * DynamicLibrary lib;
62 * if (auto result = lib.Load("my_module.so"); result) {
63 * using CreateModuleFn = Module* (*)();
64 * if (auto fn = lib.GetSymbol<CreateModuleFn>("create_module"); fn) {
65 * Module* module = (*fn)();
66 * // Use module...
67 * }
68 * }
69 * @endcode
70 */
72public:
73 /// Native handle type (void* on all platforms for ABI stability)
74 using HandleType = void*;
75 static constexpr HandleType kInvalidHandle = nullptr;
76
77 DynamicLibrary() = default;
79 DynamicLibrary(DynamicLibrary&& other) noexcept;
80
81 /**
82 * @brief Destructor that unloads the library if loaded.
83 */
84 ~DynamicLibrary() noexcept;
85
86 DynamicLibrary& operator=(const DynamicLibrary&) = delete;
87 DynamicLibrary& operator=(DynamicLibrary&& other) noexcept;
88
89 [[nodiscard]] static auto FromPath(const std::filesystem::path& path)
91
92 /**
93 * @brief Loads a dynamic library from the specified path.
94 * @param path Path to the dynamic library
95 * @return Expected with void on success, or error on failure
96 */
97 [[nodiscard]] auto Load(const std::filesystem::path& path)
98 -> std::expected<void, DynamicLibraryError>;
99
100 /**
101 * @brief Unloads the currently loaded library.
102 * @return Expected with void on success, or error on failure
103 */
104 [[nodiscard]] auto Unload() -> std::expected<void, DynamicLibraryError>;
105
106 /**
107 * @brief Reloads the library from the same path.
108 * @details Unloads the current library and loads it again.
109 * @return Expected with void on success, or error on failure
110 */
111 [[nodiscard]] auto Reload() -> std::expected<void, DynamicLibraryError>;
112
113 /**
114 * @brief Gets a raw symbol address from the library.
115 * @param name Name of the symbol to retrieve
116 * @return Expected with void pointer on success, or error on failure
117 */
118 [[nodiscard]] auto GetSymbolAddress(std::string_view name) const
119 -> std::expected<void*, DynamicLibraryError>;
120
121 /**
122 * @brief Gets a typed function pointer from the library.
123 * @tparam T Function pointer type
124 * @param name Name of the symbol to retrieve
125 * @return Expected with function pointer on success, or error on failure
126 */
127 template <typename T>
128 requires std::is_pointer_v<T>
129 [[nodiscard]] auto GetSymbol(std::string_view name) const
130 -> std::expected<T, DynamicLibraryError>;
131
132 /**
133 * @brief Checks if a library is currently loaded.
134 * @return True if a library is loaded
135 */
136 [[nodiscard]] bool Loaded() const noexcept {
137 return handle_ != kInvalidHandle;
138 }
139
140 /**
141 * @brief Gets the path of the loaded library.
142 * @return Path to the library, or empty if not loaded
143 */
144 [[nodiscard]] const std::filesystem::path& Path() const noexcept {
145 return path_;
146 }
147
148 /**
149 * @brief Gets the native handle of the loaded library.
150 * @return Native handle, or kInvalidHandle if not loaded
151 */
152 [[nodiscard]] HandleType Handle() const noexcept { return handle_; }
153
154 /**
155 * @brief Gets the last platform-specific error message.
156 * @return Error message string
157 */
158 [[nodiscard]] static std::string GetLastErrorMessage() noexcept;
159
160 /**
161 * @brief Gets the platform-specific file extension for dynamic libraries.
162 * @return ".dll" on Windows, ".so" on Linux, ".dylib" on macOS
163 */
164 [[nodiscard]] static constexpr std::string_view
166#ifdef HELIOS_PLATFORM_WINDOWS
167 return ".dll";
168#elifdef HELIOS_PLATFORM_MACOS
169 return ".dylib";
170#else
171 return ".so";
172#endif
173 }
174
175 /**
176 * @brief Gets the platform-specific library prefix.
177 * @return Empty on Windows, "lib" on Unix-like systems
178 */
179 [[nodiscard]] static constexpr std::string_view GetPlatformPrefix() noexcept {
180#ifdef HELIOS_PLATFORM_WINDOWS
181 return "";
182#else
183 return "lib";
184#endif
185 }
186
187private:
188 HandleType handle_ = kInvalidHandle; ///< Native library handle
189 std::filesystem::path path_; ///< Path to the loaded library
190};
191
193 : handle_(other.handle_), path_(std::move(other.path_)) {
194 other.handle_ = kInvalidHandle;
195}
196
198 if (Loaded()) {
199 auto _ = Unload();
200 }
201}
202
204 DynamicLibrary&& other) noexcept {
205 if (this != &other) {
206 if (Loaded()) {
207 [[maybe_unused]] auto _ = Unload();
208 }
209 handle_ = other.handle_;
210 path_ = std::move(other.path_);
211 other.handle_ = kInvalidHandle;
212 }
213 return *this;
214}
215
216inline auto FromPath(const std::filesystem::path& path)
217 -> std::expected<DynamicLibrary, DynamicLibraryError> {
218 DynamicLibrary lib;
219 if (const auto result = lib.Load(path); !result) {
220 return std::unexpected(result.error());
221 }
222 return lib;
223}
224
226 -> std::expected<void, DynamicLibraryError> {
227 if (!Loaded()) {
228 return std::unexpected(DynamicLibraryError::NotLoaded);
229 }
230
231 const auto saved_path = path_;
232
233 auto unload_result = Unload();
234 if (!unload_result) {
235 return unload_result;
236 }
237
238 return Load(saved_path);
239}
240
241template <typename T>
242 requires std::is_pointer_v<T>
243inline auto DynamicLibrary::GetSymbol(std::string_view name) const
244 -> std::expected<T, DynamicLibraryError> {
245 auto result = GetSymbolAddress(name);
246 if (!result) {
247 return std::unexpected(result.error());
248 }
249
250 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
251 return reinterpret_cast<T>(*result);
252}
253
254} // namespace helios::utils
Cross-platform dynamic library loader.
auto GetSymbol(std::string_view name) const -> std::expected< T, DynamicLibraryError >
Gets a typed function pointer from the library.
bool Loaded() const noexcept
Checks if a library is currently loaded.
static constexpr std::string_view GetPlatformExtension() noexcept
Gets the platform-specific file extension for dynamic libraries.
HandleType Handle() const noexcept
Gets the native handle of the loaded library.
auto GetSymbolAddress(std::string_view name) const -> std::expected< void *, DynamicLibraryError >
Gets a raw symbol address from the library.
static std::string GetLastErrorMessage() noexcept
Gets the last platform-specific error message.
void * HandleType
Native handle type (void* on all platforms for ABI stability).
static auto FromPath(const std::filesystem::path &path) -> std::expected< DynamicLibrary, DynamicLibraryError >
static constexpr std::string_view GetPlatformPrefix() noexcept
Gets the platform-specific library prefix.
static constexpr HandleType kInvalidHandle
auto Reload() -> std::expected< void, DynamicLibraryError >
Reloads the library from the same path.
DynamicLibrary(const DynamicLibrary &)=delete
auto Load(const std::filesystem::path &path) -> std::expected< void, DynamicLibraryError >
Loads a dynamic library from the specified path.
auto Unload() -> std::expected< void, DynamicLibraryError >
Unloads the currently loaded library.
const std::filesystem::path & Path() const noexcept
Gets the path of the loaded library.
~DynamicLibrary() noexcept
Destructor that unloads the library if loaded.
DynamicLibrary & operator=(const DynamicLibrary &)=delete
constexpr std::string_view DynamicLibraryErrorToString(DynamicLibraryError error) noexcept
Gets a human-readable description for a DynamicLibraryError.
auto FromPath(const std::filesystem::path &path) -> std::expected< DynamicLibrary, DynamicLibraryError >
DynamicLibraryError
Error codes for dynamic library operations.
@ FileNotFound
Library file not found.
@ InvalidHandle
Invalid library handle.
@ LoadFailed
Failed to load library.
@ AlreadyLoaded
Library is already loaded.
@ PlatformError
Platform-specific error.
@ SymbolNotFound
Symbol not found in library.
STL namespace.