【问题标题】:Python C interface, different modules share static variables?Python C接口,不同模块共享静态变量?
【发布时间】:2015-03-13 17:50:45
【问题描述】:

我正在使用 gcc 4.8.2 和 Python 2.7 在 Linux 中为我的自定义 C++ 库构建 python 绑定。 我的代码中有以下文件夹结构

module/
  __init__.py  
  submodule1.so # first part of lib
  submodule2.so # second part of lib
  submodule3.py # additional python tools

__init__.py

import submodule1, submodule2 submodule3

我需要在 submodule1 和 submodule2 之间传递对应于静态类成员变量的 C++ 指针。 为此,我一直在使用capsules。基本上在 submodule1 我有一个 PyObject * exportCapsule() 函数,在 submodule2 我有一个 importCapsule(PyObject *)

现在,我发现我不需要使用这些功能,我想了解原因。 我收到了 John Bollinger 的解释(见下面的回复),关于不同的 Python 模块为静态类成员变量共享相同的命名空间这一事实。

我对记录进行了完整的设置,如下所示:

文件singleton.hpp 为类似单例的行为定义静态类成员::

#ifndef _SINGLETON_HPP
#define _SINGLETON_HPP

// Singleton.hpp

// declaration of class
// + many more things

template<typename T>
class Singleton
{
private:
  static T * _ptrInstance;

public:
  static void setInstance(T* p) 
  {
    _ptrInstance = p;
  }

  static bool doesInstanceExist() 
  {
    bool output = not(NULL == _ptrInstance);
    return output;
  }

  static T* getInstance()
  {
    return _ptrInstance;
  }
};

// declaration of static class
template<typename T>
T * Singleton<T>::_ptrInstance(NULL);

#endif    

文件 submodule1.cpp 定义了第一个模块::

//submodule1.cpp

#include <Python.h>
#include "singleton.hpp"

static PyObject*errorObject;


PyObject *  exportCapsule(PyObject *dummy, PyObject *args)
{
  long * ptr = Singleton<long>::getInstance();

  const char * caps_name = "ptrInstance";
  return PyCapsule_New((void *)ptr, caps_name, NULL);
}

PyObject* setValue(PyObject* self, PyObject* args)
{
  if(not(Singleton<long>::doesInstanceExist()))
  {

    // printf("Singleton ptr %p \n",Singleton<long>::getInstance());
    // printf("Singleton is null %d \n",NULL==Singleton<long>::getInstance());
    PyErr_SetString(errorObject, "Singleton does not exist");
    return NULL;
  }


  PyObject * input;
  PyArg_ParseTuple(args, "O", &input);

  if (!PyLong_Check(input))
  {
    PyErr_SetString(errorObject, "Input should be a long integer");
    return NULL;
  }


  long * ptr = Singleton<long>::getInstance();
  *ptr = PyLong_AsLong(input);

  Py_INCREF(Py_None);
  return Py_None;
}



PyMethodDef fonctions[] = {
  {"setValue", setValue, METH_VARARGS, "set singleton value from long "},
  {"exportCapsule", exportCapsule, METH_VARARGS, "export singleton"},
  {NULL, NULL, 0, NULL}
};


PyMODINIT_FUNC initsubmodule1(void)
{

  PyObject* m = Py_InitModule("submodule1", fonctions);

  errorObject = PyErr_NewException("submodule1.Exception", NULL, NULL);

  Py_INCREF(errorObject);
  PyModule_AddObject(m, "Exception",errorObject);


  long * ptr = new long(0);
  Singleton<long>::setInstance(ptr);
}

文件submodule2.cpp 定义了第二个模块::

//submodule2.cpp

#include <Python.h>
#include "singleton.hpp"

static PyObject*errorObject;


// to be checked
PyObject *  importCapsule(PyObject *dummy, PyObject *args)
{


  const char * caps_name = "ptrInstance";

  PyObject * caps;
  PyArg_ParseTuple(args, "O", &caps);

  // we should also check the name... laziness
  if (not(PyCapsule_CheckExact(caps)))
  {
    PyErr_SetString(errorObject, "Input is not a capsule");
    return NULL;
  }


  long * ptr = (long *) PyCapsule_GetPointer(caps, caps_name);

  // if we want to set the same pointer it is ok
  if (Singleton<long>::doesInstanceExist());
  {
    long * ptrPrevious = Singleton<long>::getInstance();



    if (not(ptr == ptrPrevious))
    {
      PyErr_SetString(errorObject, "You've asked for setting the global ptr with a different value");
      return NULL;
    }
    else
    {
      PyErr_SetString(errorObject, "You've asked for setting the global ptr with same value");
      return NULL;

    }
  }

  Singleton<long>::setInstance(ptr);

  Py_INCREF(Py_None);
  return Py_None;

}


PyObject* getValue(PyObject* self, PyObject* args)
{
  if (not(Singleton<long>::doesInstanceExist()))
  {
    PyErr_SetString(errorObject, "Singleton does not exist");
    return NULL;
  }

  long val = *Singleton<long>::getInstance();

  return PyLong_FromLong(val);
}


PyMethodDef fonctions[] = {
  {"getValue", getValue, METH_VARARGS, "get long from singleton value"},
  {"importCapsule", importCapsule, METH_VARARGS, "import singleton as capsule"},
  {NULL, NULL, 0, NULL}
};


PyMODINIT_FUNC initsubmodule2(void)
{

  PyObject* m = Py_InitModule("submodule2", fonctions);

  errorObject = PyErr_NewException("submodule2.Exception", NULL, NULL);

  Py_INCREF(errorObject);
  PyModule_AddObject(m, "Exception", errorObject);


}

用于构建第一个模块的文件setup_submodule1.py::

from distutils.core import setup, Extension

submodule1 = Extension('submodule1', sources = ['submodule1.cpp'])

setup (name = 'PackageName',
        version = '1.0',
        description = 'This is a demo package',
        ext_modules = [submodule1])

用于构建第二个模块的文件setup_submodule2.py::

from distutils.core import setup, Extension

submodule2 = Extension('submodule2', sources = ['submodule2.cpp'])

setup (name = 'PackageName',
        version = '1.0',
        description = 'This is a demo package',
        ext_modules = [submodule2])

文件test.py用于测试目的::

if __name__ == "__main__":

    print '----------------------------------------------'
    print 'import submodule2'
    print 'submodule2.getValue()'

    import submodule2

    try:
        submodule2.getValue()
    except Exception, e:
        print '   ## catched :', e

    print '----------------------------------------------'
    print 'import submodule1'
    print 'submodule1.setValue(1L)'

    import submodule1
    submodule1.setValue(1L)
    print 'submodule2.getValue() ->', submodule2.getValue()

    print '----------------------------------------------'
    print 'capsule = submodule1.exportCapsule()'
    print 'submodule2.importCapsule(capsule)'

    capsule = submodule1.exportCapsule()

    try:
        submodule2.importCapsule(capsule)
    except Exception, e:
        print '   ## catched :', e

文件 Makefile 用于链接所有内容::

submodule1:
  python setup_submodule1.py  build_ext --inplace

submodule2:
  python setup_submodule2.py  build_ext --inplace

test:
  python test.py

all: submodule1 submodule2 test

make all 输出::

python test.py
----------------------------------------------
import submodule2
submodule2.getValue()
   ## catched : Singleton does not exist
----------------------------------------------
import submodule1
submodule1.setValue(1L)
submodule2.getValue() -> 1
----------------------------------------------
capsule = submodule1.exportCapsule()
submodule2.importCapsule(capsule)
   ## catched : You've asked for setting the global ptr with same value

原来的问题是:

编译后,我有两个不同的模块submodule1.sosubmodule2.so。 我可以导入它们,我不明白的是,我的胶囊东西不是必需的。这两个模块共享静态变量Singleton&lt;myClass&gt;::_ptrInstance,而不必使用胶囊导出和导入。

我怀疑这与*.so 中的符号有关。如果我打电话给nm -g *.so,我可以看到相同的符号。

我真的很惊讶两个独立编译的模块可以共享一个变量。正常吗?

我收到了一个明确的答案:这两个模块共享变量,因为命名空间对所有模块都是通用的,而我期待不同的命名空间。

【问题讨论】:

  • template&lt;typename T&gt; T * Singleton&lt;T&gt;::_ptrInstance(NULL); 实际上在标题中吗?那应该行不通。
  • 根据您的评论,我用完整的设置更新了问题。您可以找到重现我的实验所需的所有文件。

标签: python c++ c static shared-libraries


【解决方案1】:

C++ static 成员变量的全部意义在于它们在其类的所有实例之间共享。实际上,它们不属于任何实例,而是属于类本身。它们本质上是一种命名空间的全局变量。

“所有实例”是指整个程序中的所有实例,对于 Python 模块,整个程序就是 Python 解释器(即不是单个模块)。

但是,不要将静态 member 变量与静态 file-scope 变量混淆。它们的语义完全不同——实际上几乎相反。文件范围变量通常具有外部链接,这意味着声明的名称指的是该变量出现在整个程序源中的任何位置,但是该源在文件之间划分。另一方面,static 文件范围变量具有静态链接,这意味着声明的名称仅在出现声明的编译单元内引用该变量。

要点:static 成员变量是全局变量,而 static 文件范围变量是局部变量。欢迎使用 C++。

【讨论】:

  • 非常感谢您的快速回答。我知道static 成员的行为。但是,我相信不同的模块会有不同的命名空间......
猜你喜欢
  • 2017-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
  • 2011-12-09
  • 1970-01-01
  • 1970-01-01
  • 2012-06-10
相关资源
最近更新 更多