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