मैं एक परिदृश्य का परीक्षण कर रहा हूं कि जब सी ++ एक फ़ंक्शन पॉइंटर को एक पायथन क्लास वैरिएबल में सेट करता है, और उसके बाद एक और पायथन विधि चलाने के लिए PyObject_CallMethod का उपयोग करता है, जिसमें वह क्लास वैरिएबल होता है।

पूरी प्रक्रिया यही चाहेगी।

(1). PyCFunction_NewEx() एक py फ़ंक्शन बनाएं -> (2)। PyDict_SetItemString() क्लास वेरिएबल को __dict__ -> (3) के तहत असाइन करें। PyObject_CallMethod() कॉल पायथॉन मेथड विच सम्‍मिलित (1)।

जब मैं main() फ़ंक्शन (Whiteout void setCallback() के अंदर सभी कोड डालता हूं और void setCallback() के अंदर सभी कोड main()) में रखा जाता है, तो यह बिल्कुल ठीक चलता है . हालांकि, जब मैं किसी फ़ंक्शन में कुछ कोड डालता हूं, कभी-कभी सीईजी गलती मिलती है, कभी-कभी पाइथन में फ़ंक्शन पॉइंटर को कॉल नहीं करता है और कभी-कभी सही उत्तर मिलता है।

मैं इस समस्या का समाधान कैसे करूँ?

सी++ कोड: main.cpp

#include <python3.7/Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <python3.7/methodobject.h>

// func ptr.
PyObject *myCallFunc(PyObject *self,PyObject *args) {
    printf(" aaaaaaaaaaaaaaaaaaaaaaa\n");
    return NULL;
}

// def func ptr
typedef PyObject *(*PyCallFunc)(PyObject *self,PyObject *arg);

// set func ptr into python member var
void setCallback(PyObject *ClassObj){
    PyCallFunc pyCallFunc = myCallFunc;

    PyMethodDef methd = {"methd",pyCallFunc,METH_VARARGS,"py call func"};
    PyObject *fName = PyUnicode_FromString(methd.ml_name);
    if(fName == NULL) {
        printf(" fName\n");
        exit(0);
    }
    PyObject *pyRunFunc = PyCFunction_NewEx(&methd,NULL,fName);
    if(pyRunFunc == NULL){
        printf(" can not create py function. exit.");
        exit(0);
    }
    Py_DECREF(fName);

    PyObject* classAttrDict = PyObject_GetAttrString(ClassObj, "__dict__");     // extract instance Dictionary.
    if(classAttrDict == NULL) {
        printf(" classAttrDict\n");
        exit(0);
    }

    int pRetSetCurrPrice = PyDict_SetItemString(classAttrDict, "callFunc", pyRunFunc);
    if(pRetSetCurrPrice != 0){
        printf(" set error. exit.");
        exit(0);
    }
}

int main(int argc,char **argv){

    Py_SetProgramName((wchar_t *)argv[0]);
    void *pyMem = PyMem_Malloc(sizeof(wchar_t*)*argc);
    wchar_t** _argv = (wchar_t**)&pyMem;
    for (int i=0; i<argc; i++) {
        wchar_t* arg = Py_DecodeLocale(argv[i], NULL);
        _argv[i] = arg;
    }
    Py_Initialize();
    PySys_SetArgv(argc, _argv);


    PyObject* programName = PyUnicode_FromString("test");
    if(programName == NULL) {
        printf(" programName\n");
        exit(0);
    }

    PyObject* pCustomFunc = PyImport_Import(programName);   // import test
    Py_DECREF(programName);
    if(pCustomFunc == NULL) {
        printf(" pCustomFunc\n");
        exit(0);
    }
    PyObject* pClass = PyObject_GetAttrString(pCustomFunc, "Test");  // pClass = test.Test
    if(pClass == NULL) {
        printf(" pClass\n");
        exit(0);
    }    
    PyObject* pNewInstance = PyObject_CallObject(pClass,NULL);  // pNewInstance = test.Test()
    if(pNewInstance == NULL) {
        printf(" pNewInstance\n");
        exit(0);
    }

    setCallback(pNewInstance);

    PyObject* pCallRet = PyObject_CallMethod(pNewInstance, "runCustomFunc",NULL); // pCallRet = pNewInstance.callFunc()
    if(pCallRet == NULL) {
        printf(" pCallRet\n");
        //exit(0);
    }

    sleep(2);

    printf(" \n\nend\n\n");
    Py_Finalize();
    return 0;
}

पायथन कोड: test.py

import sys

def dummyFunc():
    pass

class Test:
    def __init__(self):
        self.aaa = 0
        self.callFunc = dummyFunc

    def runCustomFunc(self):
        print(" print from python.")
        print(" ref count of self.callFunc 1 is %d" %(sys.getrefcount(self.callFunc)))
        self.callFunc()
        print(" ref count of self.callFunc 2 is %d" %(sys.getrefcount(self.callFunc)))
        return 1

इस परीक्षण परियोजना के लिए cmake: CMakeLists.txt

# set cmake and compiler.
cmake_minimum_required(VERSION 3.12...3.15)
set(CMAKE_CXX_FLAGS -std=c++17)

# set variable
set(CMAKE_POSITION_INDEPENDENT_CODE ON)    # test if this can resolve the problem
set(THREADS_PREFER_PTHREAD_FLAG ON)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_FLAGS "-Wall -Wextra")    # test if optimize cause the problem
set(CMAKE_CXX_FLAGS_DEBUG "-g")         # test if optimize cause the problem
set(CMAKE_CXX_FLAGS_RELEASE "-O0")      # test if optimize cause the problem

set(LINK_LIB "/usr/local/lib")

set(PYTHON3_LINKER "-lpython3.7")
#set(PTHREAD "-lpthread")
set(PYTHON3_HEADER "/usr/include/python3.7")
set(PYTHON3_LIB "/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu")

set(CPP_FILE_LIST "main.cpp")



include_directories( ${PYTHON3_HEADER})
link_directories( ${PYTHON3_LIB} ${LINK_LIB})

add_executable(pyEmbedFunPtrTest ${CPP_FILE_LIST})

target_link_libraries(pyEmbedFunPtrTest ${PYTHON3_LINKER})

find_package(Threads REQUIRED)
target_link_libraries(pyEmbedFunPtrTest Threads::Threads)

#target_compile_options(pyEmbedFunPtrTest PUBLIC "-pthread")
2
Angel030 18 सितंबर 2020, 05:21

1 उत्तर

सबसे बढ़िया उत्तर

ऐसा इसलिए हो सकता है क्योंकि PyMethodDef, setCallback के ढेर पर बनाया गया है

आप इसे cpython के सोर्स कोड में सत्यापित कर सकते हैं यहां.

PyMethodDef की प्रतिलिपि नहीं बनाई गई है, इसके बजाय इसे संदर्भित किया गया है।

0
Szabolcs Dombi 18 सितंबर 2020, 08:30