Search code examples
c++cmemory-addressfunction-call

Call a function in C++ by its address without knowing the return type


I want to call a C function from a C++ dll by its address.

I know how to do it where the return type is known from this separate question: Calling a function through its address in memory in c / c++.

However, if I do not know the return type, what can I do? I've tried "typedef auto", but it seems you cannot use auto like that.


Solution

  • If the returning type is really unknown or it doesn't matter, you can use void in your function definition, or if it is any pointer you can use void *, but if the function is in a C coded DLL, and you'll use it in a C++ code, then you can utilize and share almost every type defined in your C code, because C++ it's a superset of C.

    That said, I prepared a small example with a structure called PyObject shared in C and C++ codes.

    To do this, is better creating a header with the shared types/definitions:

    #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
    

    Let's suppose that the C code with the exported function is something like:

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

    Finally the C++ code using the function and data created in the library would be:

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

    Edit

    As @raymondchen pointed in his comment, ignoring the return type when the C function returns a large aggregate (e.g. struct) it's not a good idea, because the C function expects that the caller already has had reserved stack space to store the returned aggregate, but if the caller treats the function as void or anything else, then compiler will not reserve that space, causing unpredictable effects (probably ending with Segmentation fault error).

    To avoid it, it's always better to define the correct type in both C and C++ codes (or in the common header), especially when the C function returns an aggregate.