如何调用一个函数,该函数用一个int向量取几个int
How do I call a function that takes several ints with a vector of ints?
问题
我正试图为一种玩具语言编写一个解释器,我希望它能够调用DLL中的函数。在某些external.dll
中,我有:
#include <cstdio>
extern "C" {
__declspec(dllexport) void print(int val) { printf("%in", val); }
__declspec(dllexport) int add(int a, int b) { return a + b; }
... more functions **that I don't know then names of**
}
假设我有一个std::string func;
,它是DLL中进程的名称,可能是"print"
或"add"
,而std::vector<int> args;
的大小是目标函数的参数数。如何相应地调用正确的DLL函数?理想情况下,我希望能够调用任何可以使用GetProcAddress
加载的函数。
我的变通方法
我目前正在使用MSVC的内联汇编程序来做我想做的事情。这是一种类似的东西
int WorkaroundCall(const std::string& func, const std::vector<int>& args) {
void* proc = GetProcAddress(hmod, func.c_str()); // hmod is the DLL's HMODULE
void* spsave, * argframe;
size_t argsize = sizeof(int) * args.size();
const int* argdata = args.data();
__asm {
mov eax, esp
sub eax, argsize
mov argframe, eax
}
memcpy(argframe, argdata, argsize);
__asm {
mov spsave, esp
mov esp, argframe
xor edx, edx
call proc
mov esp, spsave
}
}
然而,这显然不是一个好的解决方案,因为它使用Assembly并依赖于系统(有些东西告诉我这在64-bit上不起作用)。我该如何做得更好?
类似于:
#define EXTERNAL_API __declspec(dllimport)
typedef void (EXTERNAL_API* LPPRINTFN)( int );
typedef int (EXTERNAL_API* LPADDFN)( int, int );
// After loading the module you get the functions.
LPPRINTFN pfnPrint = (LPPRINTFN) GetProcAddress( hmod, "print" );
LPADDFN pfnAdd = (LPADDFN) GetProcAddress( hmod, "add" );
现在,由于您有这些字符串,您可能需要将它们映射到一个唯一的值(假设映射是全局的):
typedef enum FuncType {
Nothing = 0,
PrintFunc = 1,
AddFunc = 2
} EFuncType;
typedef map< string, EFuncType > TFuncNameMap;
TFuncNameMap funcNameMap;
if( pfnPrint != NULL ) funcNameMap["print"] = PrintFunc;
if( pfnAdd != NULL ) funcNameMap["add"] = AddFunc;
最后,调用(不包括对参数向量的任何边界检查):
int SlightlyBetterCall( const std::string& func, const std::vector<int>& args )
{
TFuncNameMap::iterator iFuncId = funcNameMap.find(func);
if( iFuncId == funcNameMap.end() )
return -1; // return some error?
int result = 0;
switch( iFuncId->second ) {
case PrintFunc:
pfnPrint( args[0] );
break;
case AddFunc:
result = pfnAdd( args[0], args[1] );
break;
}
return result;
}
你其实并不需要那张地图。。。没有什么可以阻止你去:
if( func == "print" && pfnPrint != NULL ) {
pfnPrint( args[0] );
}
else if( func == "add" && pfnAdd != NULL ) {
result = pfnAdd( args[0], args[1] );
}
这整件事对我来说似乎有点可疑,但我希望这在任何情况下都能帮助你=)
好的,得到了:-
- 您正在堆栈上创建空间,然后将偏移量传递给
proc
如果堆栈不是唯一的要求,那么最好传递堆中数据的地址。
但是,您可以使用数组在堆栈上分配空间。
int WorkaroundCall(const std::string& func, const std::vector<int>& args) {
void (*proc)(int);
void* proc = GetProcAddress(hmod, func.c_str()); // hmod is the DLL's HMODULE
void* spsave, * argframe;
size_t argsize = sizeof(int) * args.size();
const int* argdata = args.data();
int *dat= new int[argsize];
memcpy(dat, argdata, argsize);
proc(dat);
delete[] dat;
}
我普遍认为,唯一的解决方案是按照Mehrdad的建议使用LibFFI(不幸的是,它不支持MSVC),或者继续使用Assembly并维护32位和64位的不同版本(这就是我将尝试做的)。
按照Chris Becke的建议生成所有方法签名是不切实际的,因为玩具语言的类型不仅仅是int。
同样,对不起,我应该明确指出DLL中的函数名在编译时对程序来说是未知的。
在C(以及C++)中做到这一点的唯一方法是声明所有可能的函数签名,然后根据参数的数量调用适当的签名,而不需要使用汇编。
接下来需要注意的是,在64位构建中,"int"不会削减它——基本参数大小将是64位类型。由于这是一个windows问题,我将使用windowssdk中的类型来确保它在Win32和Win64中都能正确编译和工作。
#include <windows.h>
typedef INT_PTR CALLBACK Tfunc0(void);
typedef INT_PTR CALLBACK Tfunc1(INT_PTR p1);
typedef INT_PTR CALLBACK Tfunc2(INT_PTR p1,INT_PTR p2);
INT_PTR CallProc(FARPROC proc, const std::vector<INT_PTR>& args) {
switch(args.size())
{
case 0: return ((TFunc0*)proc)();
case 1: return ((TFunc1*)proc)(args[0]);
case 2: return ((TFunc2*)proc)(args[0],args[1]);
}
throw some_kind_of_exception;
}
或者,模板化的方法可以让你更容易地调用procs:
template<typename RetT>
RetT CallProc(FARPROC proc){
return (RetT)(((TFunc0*)proc)());
}
template<typename RetT,typename Param1T>
RetT CallProc(FARPROC proc, Param1T p1){
return (RetT)(((TFunc1*)proc)((INT_PTR)p1));
}
//etc.
- 调用'begin(int [n])'没有匹配函数
- 在函数中使用 const int size 参数创建数组会在 Visual Studio 中抛出错误 C++:表达式的计
- 为什么 'main' 函数中的局部 int 变量会自动初始化?
- "Missing type specifier - int assumed"无法通过向主函数添加"return 0"来解决
- 为什么 c++ 中的 main() 函数不采用除 int 和 void 之外的任何其他返回类型
- swap(int&, int&) 函数不起作用,当交换不使用临时变量时?
- 如何在不返回 C++ 值的情况下退出 int 函数?
- 调用 Parse int 函数时编译 parse 整数程序时出现问题
- 如何将字符串返回到主INT函数
- 为什么要使用int函数而不是void
- int 函数C++可以返回类似 "NULL" 的东西?
- C++int函数.当零是有效的返回值时,我如何检测false
- int const函数(参数)、int函数(const参数)和int函数(parameters)const之间的区别是什
- 类无法从主C++访问 int 函数
- 无法从 int 函数返回结果
- 如何将'int'函数调用到'void'函数
- 强制字符串为int函数以消耗整个字符串
- Int函数参数不能正常工作
- 删除Mat int函数(opencv)
- 为什么float参数适合int函数参数