Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/github/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
带递归的pythoncapi-segfaults < P>我使用C++中的Python的C API 2.7来将Python树结构转换成C++树。守则如下:_Python_Recursion_Segmentation Fault_C Api - Fatal编程技术网

带递归的pythoncapi-segfaults < P>我使用C++中的Python的C API 2.7来将Python树结构转换成C++树。守则如下:

带递归的pythoncapi-segfaults < P>我使用C++中的Python的C API 2.7来将Python树结构转换成C++树。守则如下:,python,recursion,segmentation-fault,c-api,Python,Recursion,Segmentation Fault,C Api,python树是作为一个类递归实现的,该类包含一系列子类。叶节点只是基本整数,而不是类实例 我加载一个模块并从C++调用Python方法,使用代码从中返回树的实例python树,作为C++中的pyObjix.t/p> 递归遍历获得的对象。要获取儿童列表,我需要执行以下操作: PyObject* attr = PyString_FromString("children"); PyObject* list = PyObject_GetAttr(python_tree,attr); for (int

python树是作为一个类递归实现的,该类包含一系列子类。叶节点只是基本整数,而不是类实例

我加载一个模块并从C++调用Python方法,使用代码从中返回树的实例python树,作为C++中的pyObjix.t/p> 递归遍历获得的对象。要获取儿童列表,我需要执行以下操作:

PyObject* attr = PyString_FromString("children");
PyObject* list = PyObject_GetAttr(python_tree,attr);
for (int i=0; i<PyList_Size(list); i++) {
    PyObject* child = PyList_GetItem(list,i); 
    ...
非常简单,它可以工作,直到我最终在调用PyObject_GetAttr Objects/object.c:1193时遇到分段错误,但我看不到API代码。这似乎发生在访问树的最后一个叶节点时

我很难确定这个问题。使用C API进行递归有什么特别的注意事项吗?我不确定是否需要使用Py_INCREF/Py_DECREF,或者使用函数或其他什么。老实说,我不完全理解API是如何工作的。非常感谢您的帮助

编辑:一些最小代码:

void VisitTree(PyObject* py_tree) throw (Python_exception)
{
    PyObject* attr = PyString_FromString("children");
    if (PyObject_HasAttr(py_tree, attr)) // segfault on last visit
    {
        PyObject* list = PyObject_GetAttr(py_tree,attr);
        if (list)
        {
            int size = PyList_Size(list);
            for (int i=0; i<size; i++)
            {
                PyObject* py_child = PyList_GetItem(list,i);
                PyObject *cls = PyString_FromString("ExpressionTree");
                // check if child is class instance or number (terminal)
                if (PyInt_Check(py_child) || PyLong_Check(py_child) || PyString_Check(py_child)) 
                    ;// terminal - do nothing for now
                else if (PyObject_IsInstance(py_child, cls))
                    VisitTree(py_child);
                else
                    throw Python_exception("unrecognized object from python");
            }
        }
    }
}

可以识别Python/C代码中的几个问题:

PyObject_IsInstance将类而不是字符串作为其第二个参数

没有专用于此的代码。新引用(例如PyObject_GetAttr返回的引用)永远不会发布,而使用PyList_GetItem获得的借用引用永远不会发布。将C++异常与纯Python/C混合会加重问题,使得实现正确的引用计数变得更加困难。 缺少重要的错误检查。当内存不足时,PyString_FromString可能会失败;如果列表同时缩小,PyList_GetItem可能会失败;PyObject_GetAttr在某些情况下可能会失败,即使在PyObject_HasAttr成功之后也是如此

这是一个重写但未经测试的代码版本,具有以下更改:

实用程序函数GetExpressionTreeClass从定义ExpressionTree类的模块中获取该类。为my_模块填写正确的模块名称

Guard是一个样式的Guard类,它在离开作用域时释放Python对象。这个小而简单的类使引用计数异常安全,其构造函数本身处理空对象。定义了这种风格的功能层,我建议大家看看

所有Python_异常抛出现在都伴随着设置Python异常信息。因此,Python_异常的捕获程序可以使用PyErr_PrintExc或PyErr_Fetch来打印异常,或者以其他方式找出错误所在

守则:

class Guard {
  PyObject *obj;
public:
  Guard(PyObject *obj_): obj(obj_) {
    if (!obj)
      throw Python_exception("NULL object");
  }
  ~Guard() {
    Py_DECREF(obj);
  }
};

PyObject *GetExpressionTreeClass()
{
  PyObject *module = PyImport_ImportModule("my_module");
  Guard module_guard(module);
  return PyObject_GetAttrString(module, "ExpressionTree");
}

void VisitTree(PyObject* py_tree) throw (Python_exception)
{
  PyObject *cls = GetExpressionTreeClass();
  Guard cls_guard(cls);

  PyObject* list = PyObject_GetAttrString(py_tree, "children");
  if (!list && PyErr_ExceptionMatches(PyExc_AttributeError)) {
    PyErr_Clear();  // hasattr does this exact check
    return;
  }
  Guard list_guard(list);

  Py_ssize_t size = PyList_Size(list);
  for (Py_ssize_t i = 0; i < size; i++) {
    PyObject* child = PyList_GetItem(list, i);
    Py_XINCREF(child);
    Guard child_guard(child);

    // check if child is class instance or number (terminal)
    if (PyInt_Check(child) || PyLong_Check(child) || PyString_Check(child)) 
      ; // terminal - do nothing for now
    else if (PyObject_IsInstance(child, cls))
      VisitTree(child);
    else {
      PyErr_Format(PyExc_TypeError, "unrecognized %s object", Py_TYPE(child)->tp_name);
      throw Python_exception("unrecognized object from python");
    }
  }
}

可以识别Python/C代码中的几个问题:

PyObject_IsInstance将类而不是字符串作为其第二个参数

没有专用于此的代码。新引用(例如PyObject_GetAttr返回的引用)永远不会发布,而使用PyList_GetItem获得的借用引用永远不会发布。将C++异常与纯Python/C混合会加重问题,使得实现正确的引用计数变得更加困难。 缺少重要的错误检查。当内存不足时,PyString_FromString可能会失败;如果列表同时缩小,PyList_GetItem可能会失败;PyObject_GetAttr在某些情况下可能会失败,即使在PyObject_HasAttr成功之后也是如此

这是一个重写但未经测试的代码版本,具有以下更改:

实用程序函数GetExpressionTreeClass从定义ExpressionTree类的模块中获取该类。为my_模块填写正确的模块名称

Guard是一个样式的Guard类,它在离开作用域时释放Python对象。这个小而简单的类使引用计数异常安全,其构造函数本身处理空对象。定义了这种风格的功能层,我建议大家看看

所有Python_异常抛出现在都伴随着设置Python异常信息。因此,Python_异常的捕获程序可以使用PyErr_PrintExc或PyErr_Fetch来打印异常,或者以其他方式找出错误所在

守则:

class Guard {
  PyObject *obj;
public:
  Guard(PyObject *obj_): obj(obj_) {
    if (!obj)
      throw Python_exception("NULL object");
  }
  ~Guard() {
    Py_DECREF(obj);
  }
};

PyObject *GetExpressionTreeClass()
{
  PyObject *module = PyImport_ImportModule("my_module");
  Guard module_guard(module);
  return PyObject_GetAttrString(module, "ExpressionTree");
}

void VisitTree(PyObject* py_tree) throw (Python_exception)
{
  PyObject *cls = GetExpressionTreeClass();
  Guard cls_guard(cls);

  PyObject* list = PyObject_GetAttrString(py_tree, "children");
  if (!list && PyErr_ExceptionMatches(PyExc_AttributeError)) {
    PyErr_Clear();  // hasattr does this exact check
    return;
  }
  Guard list_guard(list);

  Py_ssize_t size = PyList_Size(list);
  for (Py_ssize_t i = 0; i < size; i++) {
    PyObject* child = PyList_GetItem(list, i);
    Py_XINCREF(child);
    Guard child_guard(child);

    // check if child is class instance or number (terminal)
    if (PyInt_Check(child) || PyLong_Check(child) || PyString_Check(child)) 
      ; // terminal - do nothing for now
    else if (PyObject_IsInstance(child, cls))
      VisitTree(child);
    else {
      PyErr_Format(PyExc_TypeError, "unrecognized %s object", Py_TYPE(child)->tp_name);
      throw Python_exception("unrecognized object from python");
    }
  }
}

你检查过了所以没有空指针?你能再显示一些代码吗?可能您没有正确地检查空值或叶节点。PyObject_GetAttr返回一个新实例,因此您不必对其进行Py_递增,但请确保检查返回的值是否为null,这表示失败。PyList_GetItem返回一个借用的引用,因此不应该Py_DECREF它。如果您必须存储其结果以备将来使用,您必须使用Py_incremfB获得引用的所有权,但我认为您不必在您的情况下这样做。我在上面添加了一些代码。我已经在调试模式下进行了测试,并确保没有空指针。无法在API内部进行调试。我被卡住了。你检查过没有空指针了吗?你能展示更多的代码吗?可能您没有正确地检查空值或叶节点。PyObject
_GetAttr返回一个新实例,因此您不必对其进行Py_递增,但请确保检查返回的值是否为null,这表示失败。PyList_GetItem返回一个借用的引用,因此不应该Py_DECREF它。如果您必须存储其结果以备将来使用,您必须使用Py_incremfB获得引用的所有权,但我认为您不必在您的情况下这样做。我在上面添加了一些代码。我已经在调试模式下进行了测试,并确保没有空指针。无法在API内部进行调试。我被卡住了。谢谢,我真的很感激这个细节。我已经应用了您的更改,现在我在PyObject_IsInstancechild,cls得到一个SEGFULT。有没有办法调试python的C API代码?来源是否可用?再次感谢。我注意到,如果我在一个稍微不同的树上运行它,它甚至不会通过从python获取树的函数调用,甚至不会到达我们这里讨论的代码。在PyObject_调用中发生segfault。这让我怀疑我的python开发人员安装是否有问题。我似乎已经安装了2.6和2.7,Python/CAPI的源代码就是Python本身的源代码。如果您在Unix上运行,您可以使用./configure和make解包并编译它。也许您应该放弃py_嵌入库,直接使用Python/C API?没那么难。是的,我只是想要一些入门代码。我发现更多的成功与。谢谢,我真的很感谢细节。我已经应用了您的更改,现在我在PyObject_IsInstancechild,cls得到一个SEGFULT。有没有办法调试python的C API代码?来源是否可用?再次感谢。我注意到,如果我在一个稍微不同的树上运行它,它甚至不会通过从python获取树的函数调用,甚至不会到达我们这里讨论的代码。在PyObject_调用中发生segfault。这让我怀疑我的python开发人员安装是否有问题。我似乎已经安装了2.6和2.7,Python/CAPI的源代码就是Python本身的源代码。如果您在Unix上运行,您可以使用./configure和make解包并编译它。也许您应该放弃py_嵌入库,直接使用Python/C API?没那么难。是的,我只是想要一些入门代码。我发现我的工作更成功。