不知道返回类型的情况下,通过地址在C++中调用函数

3
我希望从一个C++ dll中通过地址调用一个C函数。
我知道如何在返回类型已知的情况下实现,可以参考这个问题:在c/c++中通过内存地址调用函数
但是,如果我不知道返回类型怎么办?我尝试了“typedef auto”,但好像不能这样使用auto关键字。

3
如果您不知道返回类型,您会预期发生什么?函数的调用方式(调用约定)取决于其返回类型。 - jtbandes
6
坏消息是,如果在编译时不知道返回类型,那么这 是不可能的 ,除非使用一大块手写汇编语言。好消息是,有人已经为您手写了汇编语言:libffi - zwol
2
如果你能说“这是一个PyObject*”,那么你在编译时就确实知道了类型,不是吗? - zwol
2
为什么你不能在typedef中使用PyObject*?你试图解决的真正问题是什么? - Guillaume Racicot
1
如果它是一个指针,你也可以在typedef中简单地使用void* - Karsten Koop
显示剩余6条评论
1个回答

2
如果返回类型确实未知或无关紧要,您可以在函数定义中使用void,或者如果它是任何指针,则可以使用void *,但如果函数在C编写的DLL中,并且您将在C ++代码中使用它,那么您可以利用并共享在C代码中定义的几乎每种类型,因为C ++是C的超集。
话虽如此,我准备了一个小例子,其中包含一个名为PyObject 的结构,可在C和C++代码中共享。
为此,最好创建一个具有共享类型/定义的头文件:
#ifndef PYOBJECT_DLL_H
#define PYOBJECT_DLL_H

#ifdef __cplusplus
extern "C" {
#endif

// Common structure definition
typedef struct PyObject{
    int field1;
    char *field2;
} PyObject;

// Public function pointer type declaration
typedef PyObject *(*__stdcall getPyObject_t)(int arg1, const char *arg2);

#ifdef __cplusplus
}
#endif

#endif  // PYOBJECT_DLL_H

假设带有导出函数的C代码如下所示:
#include "pyobject.h"
#include <stdlib.h>

#ifdef __cplusplus
extern "C" 
#endif
__declspec(dllexport) PyObject * getPyObject(int arg1, char *arg2);

PyObject *getPyObject(int arg1, char *arg2){
    PyObject *obj = (PyObject *)malloc(sizeof(PyObject));
    obj->field1 = arg1;
    obj->field2 = arg2;
    return obj;
}

最终,使用库中创建的函数和数据的C++代码将如下所示:

#include "pyobject.h"
#include <iostream>
#include <windows.h>

int main() {
    HINSTANCE dll = LoadLibrary("pyobject.dll");
    if (dll == NULL) {
        std::cerr << "Cannot open pyobject.dll. Error: " << GetLastError() << "\n";
        return 1;
    }

    getPyObject_t getPyObject = (getPyObject_t) GetProcAddress(dll, "getPyObject");
    if (getPyObject == NULL) {
        std::cerr << "Cannot locate 'getPyObject' function in dll. Error: " << GetLastError() << "\n";
        FreeLibrary(dll);
        return 2;
    }

    PyObject *obj = getPyObject(3, "test");
    std::cout << "PyObject == { field1: " << obj->field1 << ", field2: \"" << obj->field2 << "\"}\n";

    FreeLibrary(dll);
    return 0;
}

编辑

正如@raymondchen在他的评论中提到的,当C函数返回一个大的聚合类型(例如结构体)时,忽略返回类型并不是一个好主意,因为C函数期望调用者已经保留了栈空间以存储返回的聚合类型,但如果调用者将函数视为void或其他任何类型,则编译器将不会保留该空间,导致不可预测的效果(可能以段错误结束)。

为了避免这种情况,最好在C和C++代码(或公共头文件)中都定义正确的类型,特别是当C函数返回一个聚合类型时。


如果返回类型是一个大的聚合体,那么调用约定会改变。 - Raymond Chen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接