可变参数模板应用 – 动态链接库

       动态链接库的使用有两种方式,一种是在编译的时候就把动态链接库的头文件包含起来,链接的时候,系统自动去查找需要的动态链接库;另一种是程序动态的去加载,windows下是使用LoadLibrary加载库文件,然后通过GetProcAddress获取函数指针;在linux下是使用dlopen加载库文件,然后通过dlsym获取函数指针。但是动态加载的方式,需要在调用的地方定义很多函数指针,使用起来不太方便,而且动态链接库改了接口,使用的地方也需要相应的修改。有没有更好的方法呢?当然有,可以利用可变参数模板来改善动态加载链接库的使用。

windows的实现:

class DllWrapper
{
public:
    DllWrapper() : hModule(nullptr)
    {}

    ~DllWrapper()
    {
        Unload();
    }

    bool Load(const std::string& dllPath)
    {
        hModule = LoadLibraryA(dllPath.c_str());
        if(!hModule)
        {
            std::cout << "LoadLibray failed!" << std::endl;
            return false;
        }
        return true;
    }

    bool Unload()
    {
        if(hModule == nullptr)
            return true;
        
        auto ret = FreeLibrary(hModule);
        if(!ret)
            return false;

        hModule = nullptr;
        return true;
    }

    template<typename T>
    std::function<T> GetFunction(const std::string& FunName)
    {
        auto iterator = mFuncMap.find(FunName);
        if(iterator == mFuncMap.end())
        {
            auto funAddress = GetProcAddressw(hModule, FunName.c_str());
            if(!funAddress)
                return nullptr;
            
            mFuncMap.insert(std::make_paire(FunName, funAddress));
            iterator = mFuncMap.find(FuncName.c_str());
        }
        return std::function<T>((T*)(iterator->second));
    }

    template<typename T, typename... Args>
    typename std::result_of<std::function<T>(Args...)>::type ExecuteFun(const std::string& funName, Args&&... args)
    {
        auto fun = GetFunction<T>(funName);
        if(fun == nullptr)
        {
            std::string error = "cannot find this function: " + funName; 
            throw std::exception(error.c_str());
        }
            
        return f(std::forward<Args>(args)...);
    }

private:
    HMODULE hModule;
    std::map<std::string, FARPROC> mFuncMap;
};

Linux平台下只需要把平台相关的系统函数替换成dlopen,dlsym,dlclose就可以了。

#include<string>
#include<iostream>
#include<map>
#include<functional>
#include<dlfcn.h>

class DllWrapper
{
public:
    DllWrapper() : hModule(nullptr)
    {}

    ~DllWrapper()
    {
        Unload();
    }

    bool Load(const std::string& dllPath)
    {
        hModule = dlopen(dllPath.c_str(), RTLD_LAZY);
        if(!hModule)
        {
            std::cout << "LoadLibray failed! error:" << dlerror() << std::endl;
            return false;
        }
        return true;
    }

    bool Unload()
    {
        if(hModule == nullptr)
            return true;
        
        auto ret = dlclose(hModule);
        if(!ret)
            return false;

        hModule = nullptr;
        return true;
    }

    template<typename T>
    std::function<T> GetFunction(const std::string& FunName)
    {
        auto iterator = mFuncMap.find(FunName);
        if(iterator == mFuncMap.end())
        {
            auto funAddress = dlsym(hModule, FunName.c_str());
            if(!funAddress)
                return nullptr;
            
            mFuncMap.insert(std::make_pair(FunName, funAddress));
            iterator = mFuncMap.find(FunName.c_str());
        }
        return std::function<T>((T*)(iterator->second));
    }

    template<typename T, typename... Args>
    typename std::result_of<std::function<T>(Args...)>::type ExecuteFun(const std::string& funName, Args&&... args)
    {
        auto fun = GetFunction<T>(funName);
        if(fun == nullptr)
        {
            std::string error = "cannot find this function: " + funName; 
            throw std::exception(error.c_str());
        }
            
        return f(std::forward<Args>(args)...);
    }

private:
    void* hModule;
    std::map<std::string, void *> mFuncMap;
};