4

How can I tell a module compiled without -builtin that an %imported module is compiled with -builtin? Doing this naively gives me segfaults when the non-builtin module assumes that objects from the first module have wrappers.

(I never get segfaults if everything is compiled with -builtin turned off, or when using the second module alone with -builtin turned on; it's just when using them together with different compilation options.)

Details

I have several separate modules that I use SWIG for. Let's say one of them is named A, and contains fundamental objects (quaternions). Because it contains basic objects that are involved in many computations, I prefer to use SWIG's -builtin option for it. I have tested it, and this does make a pretty significant difference in timing.

Now, I also have another module named B which needs to use objects from A. But B contains big fat composite objects that I don't act on very many times, so I don't expect that there's much advantage in using -builtin here. Moreover, I really like to extend the classes in B, and do various things that are not possible with -builtin.

The problem is that I have to %import A.i inside of B.i. But then the code that's generated for B assumes that A objects have the extra wrappers, rather than using -builtin. So when I use B, I get segfaults.

(At least, I assume the segfaults result because B assumes the extra wrappers. I looked through my B_wrap.cpp file enough to see that it was assuming the presence of those wrappers, though I can't really say that I did any test to ensure that's where the problem was coming from. But the segfaults did coincide only with uses of A from B. On its own, A has never given me any trouble. Also, if I compile A and B without -builtin, I never get segfaults.)

In principle, I could use MONK's approach and just subclass any class that I need to add methods to, while compiling everything with -builtin. But this would break the nice correspondence between names in my C++ code and names in my python code, as well as requiring one or other set of users to change the names they use, as well as being a general pain in the butt.

I apologize for not having a MWE, but I think it would be an unreasonably large MWE.

Community
  • 1
  • 1
Mike
  • 19,114
  • 12
  • 59
  • 91

1 Answers1

0

I don't know that it's possible to compile with separate flags, but I have satisfied myself with MONK's solution. In combination with SWIG's %rename functionality, MONK's approach doesn't require renaming anything visible to the user. Plus, it's easy to implement, requiring just five extra lines per class I want to modify. Thus, everything gets to be compiled with -builtin, and no segfaults. Though this doesn't technically answer the question I asked at the top, it suits me.

So, let's suppose that the crucial object inside B is a class named Classy. I'll just tell SWIG to rename this to _Classy (the underscore so that I don't have to look at it every time I use tab completion in ipython). Then, I'll make an empty metaclass that subclasses those objects. Finally, I'll make a new object named Classy that just has the metaclass. So my python users and my C++ users will see it as the same object, but python users will be able to subclass it.

Here's a MWE for this part of it. A simple header:

// Classy.hpp
class Classy {
public:
  Classy() { }
};

And the SWIG file

// test.i
%module "test"

%{
  #include "Classy.hpp"
%}

%rename(_Classy) Classy;

%include "Classy.hpp"

%insert("python") %{
class _MetaClassy(type(_Classy)):
    pass
class Classy(_Classy):
    __metaclass__ = _MetaClassy
Classy.myattr = 'anything'
%}

(See how we added an attribute there at the end.) And, finally, the setup file:

# setup.py
from distutils.core import setup, Extension
example_module = Extension('_test',
                           sources=['test_wrap.cxx'])
setup (name = 'test',
       ext_modules = [example_module],
       py_modules = ["test"])

Now, just compile and test with

swig -python -builtin -c++ test.i
python setup.py build_ext --inplace
python -c 'import test; x=test.Classy(); print x.myattr'

In that last line, the python object x, which is of type Classy, does indeed have an attribute -- even though the C++ class had nothing at all. So we've succeeded.

Presumably, this subclassing defeats the speed advantage of -builtin for the Classy object, but I had already decided that I don't care about that one class. On the other hand, I get to retain the speed advantage for any objects that I don't explicitly subclass, so there is still a reason to use builtin.

Mike
  • 19,114
  • 12
  • 59
  • 91