Skip to content

File dynamic_lib.hpp

File List > illixr > dynamic_lib.hpp

Go to the documentation of this file

#pragma once

#include "error_util.hpp"

#if defined(_WIN32) || defined(_WIN64)
#    include 
#else
#    include 
#endif
#include 
#include 
#include 
#include 
#include 
#include 

namespace ILLIXR {

using void_ptr = std::unique_ptr<void, std::function<void(void*)>>;

/*
Usage:
    void* thing;
    void_ptr wrapped_thing = {thing, [](void* thing) {
        // destructor goes here.
    }}
    // wrapped_thing.get() returns underlying thing.
 */

class dynamic_lib {
public:
    dynamic_lib(dynamic_lib&& other) noexcept
        : handle_{std::move(other.handle_)}
        , library_path_{std::move(other.library_path_)} { }

    dynamic_lib& operator=(dynamic_lib&& other) noexcept {
        if (this != &other) {
            handle_       = std::move(other.handle_);
            library_path_ = std::move(other.library_path_);
        }
        return *this;
    }

    ~dynamic_lib() {
#ifndef NDEBUG
        if (!library_path_.empty()) {
            spdlog::get("illixr")->debug("[dynamic_lib] Destructing library : {}", library_path_);
        }
#endif 
    }

    static dynamic_lib create(const std::string& path) {
        return dynamic_lib::create(std::string_view{path});
    }

    static dynamic_lib create(const std::string_view& path) {
        char* error;

#if defined(_WIN32) || defined(_WIN64)
        HMODULE handle = LoadLibrary(path.data());
        if (!handle) {
            DWORD error = GetLastError();
            spdlog::get("illixr")->error(error);
            throw std::runtime_error("LoadLibrary(\"" + std::string{path} + "\"): " + std::to_string(error));
        }
#else
        // dlopen man page says that it can set errno sp
        RAC_ERRNO_MSG("dynamic_lib before dlopen");
        void* handle = dlopen(path.data(), RTLD_LAZY | RTLD_LOCAL);
        RAC_ERRNO_MSG("dynamic_lib after dlopen");

        if ((error = dlerror()) || !handle) {
            spdlog::get("illixr")->error(std::string{error});
            throw std::runtime_error{"dlopen(\"" + std::string{path} +
                                     "\"): " + (error == nullptr ? "NULL" : std::string{error})};
        }
#endif
        return dynamic_lib {
            void_ptr{handle,
                     [](void* handle) {
                         RAC_ERRNO();
#if defined(_WIN32) || defined(_WIN64)
                         int ret = FreeLibrary(static_cast<HMODULE>(handle));
                         if (ret == 0) {
                             DWORD             error = GetLastError();
                             const std::string msg_error{"dlclose(): " + std::to_string(error)};

#else
                         char* error;
                         int   ret = dlclose(handle);
                         if ((error = dlerror()) || ret) {
                             const std::string msg_error{"dlclose(): " + (error == nullptr ? "NULL" : std::string{error})};
#endif
                             spdlog::get("illixr")->error("[dynamic_lib] {}", msg_error);
                             throw std::runtime_error{msg_error};
                         }
                     }},
                std::string {
                path
            } 
        };
    }

    const void* operator[](const std::string& symbol_name) const {
        RAC_ERRNO_MSG("dynamic_lib at start of operator[]");

#if defined(_WIN32) || defined(_WIN64)
        void* symbol = GetProcAddress(static_cast<HMODULE>(handle_.get()), symbol_name.c_str());
        if (!symbol) {
            DWORD error = GetLastError();
            throw std::runtime_error{"dlsym(\"" + symbol_name + "\"): " + std::to_string(error)};
        }
#else
        char* error;
        void* symbol = dlsym(handle_.get(), symbol_name.c_str());
        if ((error = dlerror())) {
            throw std::runtime_error{"dlsym(\"" + symbol_name + "\"): " + std::string{error}};
        }
#endif
        return symbol;
    }

    template<typename T>
    T get(const std::string& symbol_name) const {
        const void* obj = (*this)[symbol_name];
        // return reinterpret_cast((*this)[symbol_name]);
        return (const T) obj;
    }

private:
    explicit dynamic_lib(void_ptr&& handle, std::string lib_path = "")
        : handle_{std::move(handle)}
        , library_path_{std::move(lib_path)} { }

    void_ptr    handle_;
    std::string library_path_;
};

} // namespace ILLIXR