Tecnología práctica C ++ 11 (5) Programación genérica Cargando función de interfaz DLL

La programación genérica de C++ 11 simplifica la carga de código dll

Formas comunes de cargar 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);//执行函数

Cargar funciones en una dll requiere tres pasos:

  • 1. Definir punteros de función
  • 2. Cargue la interfaz desde dll
  • 3. Ejecutar función

Puede que tres pasos no parezcan muchos, pero si carga cientos de funciones de este tipo, será muy tedioso y puede encontrar varios problemas, como nombres repetidos y definiciones de parámetros inconsistentes.

C ++ 11 proporciona un método para proporcionar una función universal que carga y ejecuta funciones de interfaz dll a través de programación genérica.

El código de muestra es el siguiente:

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;

El proceso anterior de cargar dll se divide en dos funciones:

  • Una es la función LoadFunction, que proporciona el puntero de función en el dll cargado a través de programación genérica.
  • Una función ExcuteFunc, utilizada para ejecutar la función cargada.

Función de carga

LoadFunction La función se utiliza para cargar punteros de función.

Acepta un  functionName parámetro que indica el nombre de la función a cargar.

Primero,  _funcMap busca para ver si el puntero de función se ha cargado y regresa directamente si lo encuentra. Si no se encuentra, use  GetProcAddress una función para obtener la dirección del puntero _dataModule de  e insértela _funcMap en el caché.

Finalmente,  std::function<T> se devuelve un objeto donde  T está el tipo del puntero de función.

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));
}

Presta atención al uso

auto addr = GetProcAddress(_dataModule, functionName.c_str());

El tipo de dirección obtenido es solo un tipo de función con parámetros de entrada vacíos y un tipo de valor de retorno void*, por lo que no se puede usar directamente. Convertiremos el puntero de función cargado al tipo de puntero de función correspondiente en el siguiente código. Cómo convertir es el punto central de este artículo .


Cuando la función finalmente devuelve el valor

return std::function<T>((T*)(it->second));

La función es  it->secondconvertir (la dirección del puntero de función) en un  T puntero de función de tipo (T es nuestro tipo de puntero de función específico) y  std::function encapsularlo con un objeto.

EjecutarFunc

ExcuteFunc La función se utiliza para ejecutar la función cargada. Acepta un  funcName parámetro, que representa el nombre de la función que se ejecutará, y un parámetro variado  args, que representa los parámetros reales de la función. Primero, llama  LoadFunction a una función para cargar el puntero de función. Si la carga falla (es decir, el puntero de función es nulo), se genera un mensaje de error. func Luego, se llama  al objeto utilizando el puntero de función cargado  std::function , pasando los parámetros dados y devolviendo el resultado de la ejecución.

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)...);
}

en función

typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)

Se utiliza una técnica de rasgos, result_of, para obtener el tipo de valor de retorno de la función.

Supongo que te gusta

Origin blog.csdn.net/weixin_44477424/article/details/132379084
Recomendado
Clasificación