Skip to content

Instantly share code, notes, and snippets.

@Guekka
Last active December 12, 2023 08:32
Show Gist options
  • Save Guekka/334e9920e745b8b7a9c0fdc1e34eda89 to your computer and use it in GitHub Desktop.
Save Guekka/334e9920e745b8b7a9c0fdc1e34eda89 to your computer and use it in GitHub Desktop.
C++ typesafe wrapper around dylib (Linux)
#pragma once
#include <dlfcn.h>
#include <iostream>
#include <mutex>
#include <stdexcept>
#include <string_view>
#include <utility>
#include <memory>
namespace macrosafe {
template<size_t Length>
struct FixedString
{
char chars[Length + 1] = {}; // +1 for null terminator
};
template<size_t N>
FixedString(const char (&arr)[N]) -> FixedString<N - 1>; // Drop the null terminator
static auto get_dlerror() -> std::string
{
static std::mutex dlerror_mutex; // dlerror is not thread-safe
auto lock = std::lock_guard(dlerror_mutex);
return dlerror(); // NOLINT(concurrency-mt-unsafe)
}
class DyLibError : public std::runtime_error
{
public:
enum class ErrorType
{
Open,
Symbol
};
explicit DyLibError(const std::string &message, ErrorType type)
: std::runtime_error(message)
, type(type)
{
}
[[nodiscard]] auto get_type() const noexcept -> ErrorType { return type; }
private:
ErrorType type;
};
template<FixedString Name, class FuncPtr>
struct DyLibFunction
{
static constexpr auto name = Name;
using type = FuncPtr;
};
class DyLib
{
static constexpr auto deleter = [](void *handle) noexcept { dlclose(handle); };
std::unique_ptr<void, decltype(deleter)> handle_;
public:
DyLib(const char *name)
: handle_(dlopen(name, RTLD_LAZY))
{
if (handle_ == nullptr)
throw DyLibError(get_dlerror(), DyLibError::ErrorType::Open);
}
template<DyLibFunction Func, typename... FuncArgs>
[[nodiscard]] constexpr auto execute(FuncArgs... args) -> decltype(auto)
{
auto *f = reinterpret_cast<decltype(Func)::type>(dlsym(handle_.get(), Func.name.chars));
if (f == nullptr)
throw DyLibError(get_dlerror(), DyLibError::ErrorType::Symbol);
return (*f)(args...);
}
};
} // namespace macrosafe
constexpr auto k_send_message_func = macrosafe::DyLibFunction<macrosafe::FixedString{"sndmsg"}, int (*)(const char *, uint16_t)>{};
// example
int main() {
DyLib lib("libclient.so");
lib.execute<k_send_message_func>("hey", 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment