C++ 嵌入 Python:将 C++ 中声明的元组传递给 Python 类的方法

1 投票
1 回答
1695 浏览
提问于 2025-04-17 09:51

把一个元组作为方法的参数传递是没问题的,但一旦我想把这个元组传给一个类的方法,就不行了(我在“ret = PyEval_CallObject(method,args);”这一行遇到了运行失败)。如果有人知道为什么这样不行,真的非常感谢。

以下是使用的代码:

enter code Python Code:

class cVector:
  def __init__(self,msg):
    self.value = msg
  def ComputeNorm(self,vecData):
    #don't use vecData for instance
    result = 12.
    return(result)

enter C++ Code

PyObject *ret, *mymod, *pclass, *method, *args, *object;
float retValue;

Py_Initialize();
PySys_SetPath(".");

// Module
mymod = PyImport_ImportModule("mModule8");
if (mymod == NULL){
  cout << "Can't Open a module:\n" ;
  Py_DECREF(mymod);
}

// Class
pclass = PyObject_GetAttrString(mymod, "cVector");
if (pclass == NULL) {
  Py_DECREF(pclass);
  cout << "Can't find class\n";
}

// Parameters/Values
args = Py_BuildValue("(f)", 100.0);
if (args == NULL) {
  Py_DECREF(args);
  cout << "Can't build argument list for class instance\n";
}

// Object with parameter/value
object = PyEval_CallObject(pclass, args);
if (object == NULL) {
  Py_DECREF(object);
  cout << "Can't create object instance:\n";
}

// Decrement the argument counter as we'll be using this again 
Py_DECREF(args);

// Get the object method - note we use the object as the object
// from which we access the attribute by name, not the class 
method = PyObject_GetAttrString(object, "ComputeNorm");
if (method == NULL) {
  Py_DECREF(method);
  cout << "Can't find method\n";
}

// Decrement the counter for our object, since we now just need
// the method reference 
Py_DECREF(object);

// Build our argument list - an empty tuple because there aren't
// any arguments 

cout << "Prepare the Tuple:\n" ;
// WE pass a tuple
args = PyTuple_New( 3 );
if (args == NULL) {
  Py_DECREF(args);
  cout << "Can't build argument list for method call\n";
}

PyObject  *py_argument;
// 1st argument 
py_argument = PyFloat_FromDouble(5.);
PyTuple_SetItem(args, 0, py_argument);

// 2nd argument 
py_argument = PyFloat_FromDouble(10.);
PyTuple_SetItem(args, 1, py_argument);

// 3nd argument 
py_argument = PyFloat_FromDouble(15.);
PyTuple_SetItem(args, 2, py_argument);

cout << "Before the Exec:\n" ;
// Call our object method with arguments 
ret = PyEval_CallObject(method,args);
//ret = PyObject_CallObject(method,args);
if (ret == NULL) {
  Py_DECREF(ret);
  cout << "Couldn't call method\n";
}


// Convert the return value back into a C variable and display it 
PyArg_Parse(ret, "f", &retValue);
printf("RetValue: %f\n", retValue);
// Kill the remaining objects we don't need 
Py_DECREF(method);
Py_DECREF(ret);
// Close off the interpreter and terminate 
Py_Finalize();  

1 个回答

2

你没有说明你是怎么得到 method 的。要让这个工作,你得从一个实例中获取它(这里我假设 inst 是一个指向 cVector 类实例的 PyObject*):

PyObject *method = PyObject_GetAttrString(inst, "ComputeNorm");

一定要检查错误:

if (method == NULL)
    return NULL;

(或者根据具体情况做其他合适的处理)

这样,你的代码可以大大简化:

PyObject *args = Py_BuildValue("(ddd)", 5.0, 10.0, 15.0);

(这会创建一个包含三个由 C 的双精度浮点数转换而来的 Python 浮点数的元组)

甚至可以和调用结合在一起:

PyObject *ret = PyObject_CallFunction(method, "(ddd)", 5.0, 10.0, 15.0);

你甚至可以把所有东西合并成一次调用:

PyObject *ret = PyObject_CallMethod(inst, "ComputeNorm",
                                    "(ddd)", 5.0, 10.0, 15.0);

再次提醒,记得检查错误:

if (ret == NULL)
   return NULL;

而且一定要释放你创建但不再需要的所有对象(否则你会造成内存泄漏):

Py_DECREF(ret);

(假设你使用了 PyObject_CallMethod,否则你可能还需要释放 argsmethod

撰写回答