Short version
I don't understand how to reliably initialize static members for a python extension. In particular, how can I ensure a particular load order for code from different compilation units, so that certain dependencies are satisfied?
Long version
I have some code that computes various combinatoric functions many times, like the factorial, binomial, etc. It's much more efficient to just construct a table for these things once, and then do the lookup on them. And I need a nice interface for getting the values, so I just make c++ classes, with the tables as static members.
As the simplest example, to compute factorials, I have the following. In Combinatorics.hpp
:
class FactorialFunctor {
private:
static const std::vector<double> FactorialTable;
public:
FactorialFunctor() { };
inline double operator()(const unsigned int i) const {
return FactorialTable[i];
}
};
In Combinatorics.cpp
:
const std::vector<double> FactorialFunctor::FactorialTable = FactorialTableCalculator();
where FactorialTableCalculator
is a local function that just returns the appropriate vector. [Those operator()
s get more complicated for binomials, etc., which is why I use classes to wrap these things.]
I use SWIG to wrap the c++
code up, and use it from python. This was all going well until I compiled my code on a new cluster that I'm using. Now I think I was just getting lucky previously.
As soon as I imported my python module on the new cluster, python would segfault. Python didn't even get back from the import step. Using gdb
, I tracked this down to another part of the code, whose initialization calls a Factorial
functor. But the FactorialTable
hadn't been initialized yet, so the whole thing croaked.
So I need to ensure that my factorial is calculated before that other code is calculated. I tell distutils about it in the desired order, but obviously this isn't the order they get called on this cluster. Is there some stage in the linking process that I need to be careful with or something?
If you're really motivated, you can browse the code here and here, and the line that gives the segfault here.