C++11 generic programming simplifies loading dll code
Common ways to load dll:
HMODULE m_hDataModule;
m_hDataModule = LoadLibrary("myDll.dll");
typedef int (*PfunA)(int a, int b);//定义函数指针
PfunA fun = (PfunA)(GetProcAddress(m_hDataModule , "funA"));//加载接口
int ret = fun(1, 2);//执行函数
Loading functions in a dll requires three steps:
- 1. Define function pointers
- 2. Load the interface from dll
- 3. Execute function
Three steps may not seem like many, but if you load hundreds of such functions, it will be very tedious, and you may encounter various problems such as repeated naming and inconsistent parameter definitions.
C++11 provides a method to provide a universal function that loads and executes dll interface functions through generic programming.
The sample code is as follows:
std::map<string, FARPROC> _funcMap;
HMODULE _dataModule;
template <typename T>
std::function<T> LoadFunction(const string& functionName)
{
auto it = _funcMap.find(functionName);
if (it == _funcMap.end())
{
auto addr = GetProcAddress(_dataModule, functionName.c_str());
if (!addr) return
nullptr;
_funcMap.insert(std::make_pair(functionName, addr));
it = _funcMap.find(functionName);
}
return std::function<T>((T*)(it->second));
}
template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)
{
auto func = LoadFunction<T>(funcName);
if (func == nullptr)
{
std::cout << "Load " << funcName << " error!" << std::endl;
}
return func(std::forward<Args>(args)...);
}
int main()
{
_dataModule = LoadLibrary("myDll.dll");
//一行代码即可加载并执行接口,并且支持各种类型的接口函数
int funA = ExcuteFunc<int(int, int)>("funA", 1, 2);//有两个入参,返回int类型
bool funB = ExcuteFunc<bool(int)>("funB", 1);//有一个入参,返回bool类型
ExcuteFunc<void()>("funC");//没有入参,也没有返回值
return 0;
The above process of loading dll is divided into two functions:
- One is the LoadFunction function, which provides the function pointer in the loaded dll through generic programming.
- An ExcuteFunc function, used to execute the loaded function.
LoadFunction
LoadFunction
Function is used to load function pointers.
It accepts a functionName
parameter indicating the name of the function to be loaded.
First, it _funcMap
searches to see if the function pointer has been loaded, and returns directly if found. If it is not found, use GetProcAddress
a function to get the address of the function pointer _dataModule
from it and insert it _funcMap
into the cache.
Finally, an std::function<T>
object is returned where T
is the type of the function pointer.
template <typename T>
std::function<T> LoadFunction(const string& functionName)
{
auto it = _funcMap.find(functionName);
if (it == _funcMap.end())
{
auto addr = GetProcAddress(_dataModule, functionName.c_str());
if (!addr) return
nullptr;
_funcMap.insert(std::make_pair(functionName, addr));
it = _funcMap.find(functionName);
}
return std::function<T>((T*)(it->second));
}
Pay attention to use
auto addr = GetProcAddress(_dataModule, functionName.c_str());
The obtained addr type is just a function type with empty input parameters and void* return value type, so it cannot be used directly. We will convert the loaded function pointer to the corresponding function pointer type in the following code. How to convert is the core point of this article .
When the function finally returns the value
return std::function<T>((T*)(it->second));
The function is to it->second
coerce (the address of the function pointer) into a function T
pointer of type (T is our specific function pointer type) and std::function
encapsulate it with an object.
ExecuteFunc
ExcuteFunc
Function is used to execute the loaded function. It accepts a funcName
parameter, which represents the name of the function to be executed, and a variadic parameter args
, which represents the actual parameters of the function. First, it calls LoadFunction
a function to load the function pointer. If the loading fails (that is, the function pointer is null), an error message is output. func
Then, the object is called using the loaded function pointer std::function
, passing the given parameters, and returning the execution result.
template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)
{
auto func = LoadFunction<T>(funcName);
if (func == nullptr)
{
std::cout << "Load " << funcName << " error!" << std::endl;
}
return func(std::forward<Args>(args)...);
}
in function
typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)
A traits technique, result_of, is used to obtain the return value type of the function.