1

I have a commercial program that simulates some stuff. The program allows me to use a DLL so it can send me the simulation output at each time step so the DLL can calculate some stuff(using control theory) and send back to the simulation the input at t+1. My control functions are written in Python, so i would like to use the C DLL to: receive the program simulation output, call Python functions and send to the simulation the Python function output. Code workingdirectly in DLL, only returns to sim input 2 times the sim output:

''// Variables:
  //      t: Time, passed from Program by value
  //   delt: Time step, passed from Program by value
  //     in: input array, passed from Program by reference
  //    out: output array, sent back to Program (Note: the values of out[*] can
  //         be modified in Program)
  // Because we used static/global variables in this example, the DLL 
  // can only be used once per schematic file.  
  #include <math.h>
  __declspec(dllexport) void simuser(double t, double delt, double* in,double*out)
   {
       out[0] = 2*in[0];
  }

''

Budelon
  • 53
  • 5
  • Does this answer your question? [How can I use a DLL file from Python?](https://stackoverflow.com/questions/252417/how-can-i-use-a-dll-file-from-python) – Aaron Jan 08 '20 at 14:54
  • @Aaron Read. It says "call python function from C DLL", not the other way around. – S.S. Anne Jan 08 '20 at 14:59
  • @Budelon in order to call your own code, you'll need an entry point. Is there some sort of function within the DLL that allows you to register a callback? – Aaron Jan 08 '20 at 15:05
  • @Aaron i have a code that works that only multiplies the output simulation by 2 and send back as simulation input. ''// Variables: // in: input array, passed from Program by reference // out: output array, sent back to Program (Note: the values of out[*] can // be modified in Program) // Because we used static/global variables in this example, the DLL // can only be used once per schematic file. #include __declspec(dllexport) void simuser(double t, double delt, double* in, double* out) { out[0] = 2*in[0]; } – Budelon Jan 08 '20 at 15:12
  • Is this helpful? https://stackoverflow.com/questions/21011620/c-dll-to-python-callback If you have a working minimal example, perhaps you could post that code and I could get a better idea of where you're getting stuck – Aaron Jan 08 '20 at 15:14
  • Ok, do I have this correct now? you are writing your own c++ DLL which your commercial software calls, and you'd like this c++ dll to call your existing python code. – Aaron Jan 08 '20 at 15:22
  • @Aaron i edited just now the question with my DLL code getting the simulation output and just multiplying by 2 and sending back to the Program. The Python code should replace that multiplication with some more complex algebra and optimizations etc. – Budelon Jan 08 '20 at 15:26
  • @Aaron Yes, it's exactly what i would like to have. – Budelon Jan 08 '20 at 15:27
  • @Budelon I'm writing up a quick example based on [this](https://docs.python.org/3.7/extending/embedding.html#pure-embedding) It's a little bit of new territory for me, so it'll probably have errors, but it will hopefully get you down the right path. – Aaron Jan 08 '20 at 15:42
  • @Aaron i've just commented with my code that worked fine. Thanks!! – Budelon Jan 16 '20 at 01:41

2 Answers2

0

I modified and commented the example from https://docs.python.org/3.7/extending/embedding.html#pure-embedding to help you get started. The function itself still does the same thing at the very last line (out[0] = 2*in[0];) because it'll be up to you to structure the data you're sending to the python function and then to unpack it to the output structure you want. What it does do is import a library called mymodule then call mymodule.myfunction(10,42) and recieves the result as a Long Here's the official reference for the rest of the python c api. I'm not really a c/c++ developer, so this probably has some glaring errors, but the structure should be there for you to get started.

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <math.h> 

__declspec(dllexport) void simuser(double t, double delt, double in, double* out) {
    //copied and modified from https://docs.python.org/3.7/extending/embedding.html#pure-embedding

    //typedefs
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    //spin up an interpreter
    Py_Initialize();

    //create a python string of the library you want to import
    pName = PyUnicode_DecodeFSDefault("mymodule");
    /* Error checking of pName left out */

    //import our library
    pModule = PyImport_Import(pName);

    //garbage collect module string which is no longer needed
    Py_DECREF(pName);

    //if we successfully loaded the library
    if (pModule != NULL) {
        //get a function from that library by name
        pFunc = PyObject_GetAttrString(pModule, "myfunction");
        /* pFunc is a new reference */

        //check if we got anything, and that it's callable (a function)
        if (pFunc && PyCallable_Check(pFunc)) {
            //create a tuple where we'll put (2) arguments to the python funciton we want to call
            pArgs = PyTuple_New(2);

            //cast a string to Long then to PyLong
            pValue = PyLong_FromLong(atoi("10"));

            //set it as the first element of the tuple
            PyTuple_SetItem(pArgs, 0, pValue);

            //cast another string as second argument
            pValue = PyLong_FromLong(atoi("42"));

            //set it as the second element of the tuple
            PyTuple_SetItem(pArgs, 1, pValue);

            //call your function with the tuple of arguments we created
            pValue = PyObject_CallObject(pFunc, pArgs);

            //garbage collect arguments
            Py_DECREF(pArgs);

            //if call returned without error
            if (pValue != NULL) {

                //convert output of funciton to regular Long from PyLong and print
                printf("Result of call: %ld\n", PyLong_AsLong(pValue));
                //garbage collect
                Py_DECREF(pValue);
            }
            else {
                //garbage collect
                Py_DECREF(pFunc);
                //garbage collect
                Py_DECREF(pModule);
                //print a useful error message hopefully and exit nonzero
                PyErr_Print();
                fprintf(stderr,"Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"myfunction\"\n");
        }
        //garbage collect
        Py_XDECREF(pFunc);
        //garbage collect
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    }
    if (Py_FinalizeEx() < 0) {
        return 120;
    }
    return 0;


    out[0] = 2*in[0]; //actual function isn't changed because I don't know what your data looks like, but a python function is still called in the process
}
Aaron
  • 10,133
  • 1
  • 24
  • 40
0

I have a code now that works fine as main.c; I'm now trying to embed this function main() inside my .dll file to make this available to my commercial program.

int main(int argc, const char** argv)
  {
//Inicializa
Py_SetPythonHome(pylibs);
Py_SetPath(pypath);
PyObject* pName, * pModule, * pFunc;
PyObject* pArgs, * pValue;
float resultPython;


Py_Initialize();
//printf("deu");

// Code para Wrappping


pName = PyUnicode_DecodeFSDefault("reverse");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
    printf("pModule nao e null \n");
}
pFunc = PyObject_GetAttrString(pModule, "abc");
if (pFunc == NULL)
{
    Py_DECREF(pFunc);
    printf("Couldn't call method");
}

if (pFunc && PyCallable_Check(pFunc)) {


    //int argumento = 1;
    pArgs = Py_BuildValue("(i)", 3);
    //pArgs = Py_BuildValue("bla bla");
    pValue = PyEval_CallObject(pFunc, pArgs);
    PyErr_Print();
    if (pValue != NULL) {
        PyArg_Parse(pValue, "f", &resultPython);
        printf("Result of call: %f\n", resultPython);
        Py_DECREF(pValue);
    }
    else {
        printf("recebi NULL de pValue");
    }

}
else {
    printf("bla bla");
}

Py_Finalize();

return 0;
 }
Budelon
  • 53
  • 5