Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
dynamic_library.cpp
Go to the documentation of this file.
2
4
5#include <expected>
6#include <filesystem>
7#include <string>
8#include <string_view>
9
10#ifdef HELIOS_PLATFORM_WINDOWS
11#ifndef WIN32_LEAN_AND_MEAN
12#define WIN32_LEAN_AND_MEAN
13#endif
14#ifndef NOMINMAX
15#define NOMINMAX
16#endif
17#include <windows.h>
18#elif defined(HELIOS_PLATFORM_LINUX) || defined(HELIOS_PLATFORM_MACOS)
19#include <dlfcn.h>
20#else
21#error "Unsupported platform for dynamic library loading"
22#endif
23
24namespace helios::utils {
25
26auto DynamicLibrary::Load(const std::filesystem::path& path) -> std::expected<void, DynamicLibraryError> {
27 if (Loaded()) {
28 return std::unexpected(DynamicLibraryError::AlreadyLoaded);
29 }
30
31 if (!std::filesystem::exists(path)) {
32 return std::unexpected(DynamicLibraryError::FileNotFound);
33 }
34
35#ifdef HELIOS_PLATFORM_WINDOWS
36 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
37 handle_ = reinterpret_cast<HandleType>(LoadLibraryW(path.wstring().c_str()));
38#else
39 handle_ = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
40#endif
41
42 if (handle_ == kInvalidHandle) {
43 HELIOS_ERROR("Failed to load library '{}': {}!", path.string(), GetLastErrorMessage());
44 return std::unexpected(DynamicLibraryError::LoadFailed);
45 }
46
47 path_ = path;
48 HELIOS_DEBUG("Loaded dynamic library: {}", path_.string());
49 return {};
50}
51
52auto DynamicLibrary::Unload() -> std::expected<void, DynamicLibraryError> {
53 if (!Loaded()) {
54 return std::unexpected(DynamicLibraryError::NotLoaded);
55 }
56
57 bool success = false;
58#ifdef HELIOS_PLATFORM_WINDOWS
59 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
60 success = FreeLibrary(reinterpret_cast<HMODULE>(handle_)) != 0;
61#else
62 success = dlclose(handle_) == 0;
63#endif
64
65 if (!success) {
66 HELIOS_ERROR("Failed to unload library '{}': {}!", path_.string(), GetLastErrorMessage());
67 return std::unexpected(DynamicLibraryError::PlatformError);
68 }
69
70 HELIOS_DEBUG("Unloaded dynamic library: {}", path_.string());
71 handle_ = kInvalidHandle;
72 path_.clear();
73 return {};
74}
75
76auto DynamicLibrary::GetSymbolAddress(std::string_view name) const -> std::expected<void*, DynamicLibraryError> {
77 if (!Loaded()) {
78 return std::unexpected(DynamicLibraryError::NotLoaded);
79 }
80
81 // Need null-terminated string for platform APIs
82 std::string name_str(name);
83
84 void* symbol = nullptr;
85#ifdef HELIOS_PLATFORM_WINDOWS
86 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
87 symbol = reinterpret_cast<void*>(GetProcAddress(reinterpret_cast<HMODULE>(handle_), name_str.c_str()));
88#else
89 symbol = dlsym(handle_, name_str.c_str());
90#endif
91
92 if (symbol == nullptr) {
93 HELIOS_WARN("Symbol '{}' not found in library '{}': {}!", name, path_.string(), GetLastErrorMessage());
94 return std::unexpected(DynamicLibraryError::SymbolNotFound);
95 }
96
97 return symbol;
98}
99
101#ifdef HELIOS_PLATFORM_WINDOWS
102 DWORD error_code = GetLastError();
103 if (error_code == 0) {
104 return "No error";
105 }
106
107 LPSTR message_buffer = nullptr;
108 const size_t size = FormatMessageA(
109 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code,
110 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr);
111
112 std::string message(message_buffer, size);
113 LocalFree(message_buffer);
114
115 // Remove trailing newline
116 while (!message.empty() && (message.back() == '\n' || message.back() == '\r')) {
117 message.pop_back();
118 }
119
120 return message;
121#else
122 const char* error = dlerror();
123 return error != nullptr ? std::string(error) : "No error";
124#endif
125}
126
127} // namespace helios::utils
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)
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.
#define HELIOS_DEBUG(...)
Definition logger.hpp:667
#define HELIOS_ERROR(...)
Definition logger.hpp:689
#define HELIOS_WARN(...)
Definition logger.hpp:687
@ FileNotFound
Library file not found.
@ LoadFailed
Failed to load library.
@ AlreadyLoaded
Library is already loaded.
@ NotLoaded
Library is not loaded.
@ PlatformError
Platform-specific error.
@ SymbolNotFound
Symbol not found in library.