【问题标题】:C++ statics in Boost.PythonBoost.Python 中的 C++ 静态
【发布时间】:2014-05-06 09:14:29
【问题描述】:

我有一个 C++ 类,其中包含某种“静态”(在这种特殊情况下为“m_engine”):

class RndGenerator
{
public:
  static void setInitialSeed(unsigned int seed);
  static unsigned int rndNumber();
  ...
private:
  ...
  RndGenerator();
  static std::mt19937 m_engine;
};

这个类在我的项目中被广泛使用,在 C++ 级别。

通过 Boost.Python 暴露 RndGenerator 后:

class_<RndGenerator, boost::noncopyable>("RndGenerator", no_init)
  .def("setInitialSeed", &RndGenerator::setInitialSeed)
  .staticmethod("setInitialSeed")
  .def("rndNumber", &RndGenerator::rndNumber)
  .staticmethod("rndNumber")
  ;

我希望有可能从 Python 级别设置初始种子:

 RndGenerator.setInitialSeed(1234)

我希望,在这一行之后,在 C++ 级别上对 RndGenerator::rndNumber() 的所有调用都会考虑刚刚指定的初始种子 (1234)。 然而事实并非如此。

包含暴露给 Python 的静态成员的类有什么问题吗? 还是我的问题与 RndGenerator 的单例性质有关?

【问题讨论】:

    标签: c++ boost-python


    【解决方案1】:

    Boost.Python 在暴露的 C++ 类上使用静态数据成员或静态成员函数应该没有问题。这有可能是假阳性吗?或者,对于在多个翻译单元中实例化同一模板的更复杂和特定的情况,使用动态库,具有相同符号名称的静态数据成员的多个实例可能驻留在同一进程空间中。

    无论如何,这里有一个完整的示例,展示了使用 Boost.Python 公开的类上的静态成员函数和静态数据成员的预期行为:

    #include <boost/python.hpp>
    
    // Basic mockup type.
    class spam
    {
    public:
      static void set_x(unsigned int x) { x_ = x; }
      static unsigned int get_x() { return x_; };
    private:
      spam() {};
      spam(const spam&);
      spam& operator=(const spam&);
      static unsigned int x_;
    };
    
    unsigned int spam::x_ = 0;
    
    // Auxiliary function.
    bool spam_check_x(unsigned int x)
    {
      return x == spam::get_x();
    }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::class_<spam, boost::noncopyable>("Spam", python::no_init)
        .def("setX", &spam::set_x)
          .staticmethod("setX")
        .def("getX", &spam::get_x)
          .staticmethod("getX")
        .def("checkX", &spam_check_x)
          .staticmethod("checkX")
        ;
    }
    

    互动使用:

    >>> from example import Spam
    >>> x = 42
    >>> assert(Spam.getX() != x)
    >>> assert(not Spam.checkX(x))
    >>> Spam.setX(x)
    >>> assert(Spam.getX() == x)
    >>> assert(Spam.checkX(x))
    >>> x = 43
    >>> assert(Spam.getX() != x)
    >>> assert(not Spam.checkX(x))
    

    【讨论】:

    • 感谢您的回复。就我而言,我怀疑这可能与同一进程中的多个静态数据成员实例有关。
    • @user765572 当我遇到此类问题时,我记录了寻址的内存以确认存在差异。一些编译器和链接器需要为类提供存储信息,如this 答案所示。
    【解决方案2】:

    我意识到这是一个老问题,但我最近遇到了同样的问题并认为我应该分享。

    我们能够通过以下步骤在 Boost 1.56 的 Windows 上重现该问题:

    • 在 C++ 中,创建两个单独的 Boost.Python 模块:
      • 模块 A 公开了一个函数来设置类 Foo 中的静态变量
      • 模块 B 公开了一个函数来访问 Foo 的静态变量
    • 在 Python 中,调用 A 的 setter,然后调用 B 的 getter。您可能希望该值匹配,但(至少在我们的例子中)B 的静态仍将设置为其默认值。

    这只会打破扩展模块边界(单独的 .pyd 文件)。该问题与 Windows 上的动态链接行为有关。 Here 是一个讨论同一问题的不同表现形式的 wiki 页面。

    我们的解决方案是将 C++ 模块合并到一个 Boost.Python 模块中,因此是一个共享对象。为了模仿我们的旧设置,我们添加了子模块来保持库之间的分离:

    // Put all following definitions in the current scope into a namespace-esque module
    #define MAKE_MODULE(modName) \
        boost::python::object module(boost::python::handle<>(boost::python::borrowed(PyImport_AddModule("pymod."#modName)))); \
        scope().attr(#modName) = module; \
        scope s(module);
    
    using namespace boost::python;
    
    BOOST_PYTHON_MODULE(pymod)
    {
        object package = scope();
        package.attr("__path__") = "pymod";
    
        // pymod.a
        {
            MAKE_MODULE(a);
            exportModuleA();
        }
    
        // pymod.b
        {
            MAKE_MODULE(b);
            exportModuleB();
        }
    
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 1970-01-01
      • 2012-09-07
      • 2010-10-30
      • 1970-01-01
      • 2016-04-29
      • 2011-05-30
      相关资源
      最近更新 更多