C++静态成员在Python扩展模块中的初始化

1 投票
1 回答
613 浏览
提问于 2025-04-18 03:27

简短版本

我不太明白如何可靠地初始化Python扩展中的静态成员。特别是,如何确保来自不同编译单元的代码按照特定的加载顺序执行,以满足某些依赖关系?

详细版本

我有一些代码需要多次计算各种组合函数,比如阶乘、二项式等。为了提高效率,我只需要构建一个表格来存储这些值,然后在需要的时候查找它们。而且我需要一个简单的接口来获取这些值,所以我就创建了C++类,把这些表格作为静态成员。

举个简单的例子,计算阶乘时,我有以下代码。在 Combinatorics.hpp 文件中:

  class FactorialFunctor {
  private:
    static const std::vector<double> FactorialTable;
  public:
    FactorialFunctor() { };
    inline double operator()(const unsigned int i) const {
      return FactorialTable[i];
    }
  };

Combinatorics.cpp 文件中:

const std::vector<double> FactorialFunctor::FactorialTable = FactorialTableCalculator();

这里的 FactorialTableCalculator 是一个本地函数,它只返回合适的向量。[那些 operator() 的实现对于二项式等会更复杂,所以我才使用类来封装这些东西。]

我使用SWIG将C++代码封装起来,并在Python中使用。之前一切都很顺利,直到我在一个新的集群上编译我的代码。现在我觉得之前的成功可能只是运气好。

当我在新集群上导入我的Python模块时,Python就崩溃了。Python甚至没有完成导入步骤。通过使用 gdb,我发现问题出在代码的另一部分,它的初始化调用了一个 Factorial 函数对象。但此时 FactorialTable 还没有初始化,所以整个程序就崩溃了。

因此,我需要确保在其他代码计算之前先计算出阶乘。我在distutils中告诉它所需的顺序,但显然在这个集群上并不是按照这个顺序调用的。我需要在链接过程中注意什么阶段吗?

如果你有兴趣,可以浏览代码,这里这里,导致崩溃的那一行在这里

1 个回答

2

没有可靠的方法可以强制静态成员的初始化顺序。

不过,从代码来看,我觉得你需要的不是一个静态成员,而是一个单例模式。在C++中,单例模式通常是通过一些函数来实现的,像这样:

FactorialFunctorData& _FactorialFunctorData()
{
    static FactorialFunctorData data_;
    return data_;
}

这样一来,你就可以在执行时按需要的顺序调用这些函数,从而控制执行的顺序。

撰写回答