Python C接口,不同模块共享静态变量?

Python C interface, different modules share static variables?

我在 Linux 中使用 gcc 4.8.2 和 Python 2.7 为我的自定义 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之间传递静态class成员变量对应的C++指针。 为此,我一直在使用 capsules。基本上在 submodule1 中我有一个 PyObject * exportCapsule() 函数,在 submodule2 中我有一个 importCapsule(PyObject *)

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

我包装了一个完整的设置记录如下:

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

#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

原问题是:

After compilation, I have two different modules submodule1.so and submodule2.so. I can import them, and what I dont understand, is that my capsule stuff is not required. The two modules share the static variable Singleton<myClass>::_ptrInstance, without having to use the capsule export and import.

I suspect that it has to do with the symbols within both *.so. If I call nm -g *.so I can see identical symbols.

I am really amazed that two independently compiled modules can share a variable. Is it normal ?

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

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

"All instances"表示整个程序中的所有实例,对于Python模块,整个程序就是Python解释器(即不是单独的模块)。

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

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