C++11 Practical Technology (5) Generic Programming Loading DLL Interface Function

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->secondcoerce (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.

Guess you like

Origin blog.csdn.net/weixin_44477424/article/details/132379084