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

标签 python c++ c static shared-libraries

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

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()函数,在子模块 2 中我有一个 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

原来的问题是:

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 成员变量的全部意义在于它们在其类的所有实例之间共享。事实上,它们不属于任何实例,而是属于类本身。它们本质上是命名空间全局变量的一种形式。

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

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

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

关于Python C接口(interface),不同模块共享静态变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29038794/

相关文章:

python - 如何获取没有目录名的文件名

python - 对 .dat 文件的两列进行一些数学计算并将结果保存到第三列

Python 等价于指针

python - 将 .cpp 文件中的函数导入到 python 中

c++ - 在派生类中复制类似构造函数的基类实例复制

c++ - 基于 SonarQube 和 Qt 的资源

python - 在 (i)python session 之间保留命令历史记录

c++ - 在 C/C++ 中创建和发送原始数据包

c - pthread_exit() 和 exit() 之间的区别?

c - 制作一个简单的HTTP网络服务器,使用errno回复 "403 Forbidden",然后重定向到403页面?