spencerlyon2
spencerlyon2

Reputation: 9686

SWIG c++ python constructor wrong type (possible namespace issue)

I have a class that is implemented entirely in a header file (HKnotVector.h). I also have a SWIG interface file (HKnotVector.i) that defines some std_vector.i typemaps. There are definitions HKnotVector.h needs from another file (common.h). If I copy/paste the relevant parts of common.h into HKnotVector.i, everything works perfectly.

The problem comes when I try to #include "common.h" inside HKnotVector.h. SWIG and python are able to build the extension, but I get the following warning: HKnotVector.h:7: Error: Nothing known about namespace 'util'

Here are relavent parts of HKnotVector.h:

/* file HKnotVector.h */
#include "common.h"
#include <vector>
#include <iostream>

using namespace std;
using namespace util;

namespace hbs
{
    class HKnotVector
    {
        public:
        HKnotVector( uint degree, const DoubleVec &knots )
            : mDeg( degree ), mKnots( knots )
        {
            getKVecData( mKnots, mGroups, mReverseGroups, mMultipleCount );
        }
        // A lot of missing code
        protected:
        uint mDeg;
        DoubleVec mKnots;
        IntVec mGroups;
        IntVecVec mReverseGroups;
        IntVec mMultipleCount;
        void getKVecData( const DoubleVec &knots, IntVec &knot_groups,
                          IntVecVec &reverse_knot_groups, IntVec &multiple_counts ) const
        {
            // Do cool stuff
        }
    };
}

The relevant parts of common.h:

/* file common.h */
#include <iostream>
#include <vector>

typedef unsigned int uint;

using namespace std;

namespace util
{
    typedef std::vector< double > DoubleVec;
    typedef std::vector< int > IntVec;
    typedef std::vector< IntVec > IntVecVec;
    // A whole bunch of more stuff
}

All of HKnotVector.i:

/* file HKnotVector.i */
%module hbspy
%{
#include "HKnotvector.h"
%}

%include "std_vector.i"
namespace std {
    %template(IntVec)    vector<int>;
    %template(DoubleVec) vector<double>;
    %template(IntVecVec) vector<vector<int> >;
}

%include "HKnotvector.h"

Finally I have the test python file that attempts to create an instance of HKnotVector:

import hbspy

# Test HKnotVector
py_knots = [0., 0., 0., .25, .5, .75, 1., 1., 1.]
knots = hbspy.DoubleVec(py_knots)
print('knots object: %s\n\n\n' % knots)
kv = hbspy.HKnotVector(2, knots)

When I compile using swig -c++ -python HKnotVector.i then build the extension and run the test file this is what I get:

knots object: <hbspy.DoubleVec; proxy of <Swig Object of type 'std::vector< double > *' at 0x100499450> >

Traceback (most recent call last):
File "./doit", line 17, in <module>
  execfile('test.py')
File "test.py", line 18, in <module>
  kv = hbspy.HKnotVector(2, knots)
File "/Users/spencerlyon2/Research/HBS/hbs/swig/hbspy.py", line 249, in __init__
  this = _hbspy.new_HKnotVector(*args)
NotImplementedError: Wrong number or type of arguments for overloaded function 'new_HKnotVector'.
Possible C/C++ prototypes are:
  hbs::HKnotVector::HKnotVector()
  hbs::HKnotVector::HKnotVector(uint,DoubleVec const &)
  hbs::HKnotVector::HKnotVector(hbs::HKnotVector const &)

When I print knots you can see that it is of type std::vector < double > *, which in common.h was typdefd as DoubleVec. I can't figure out why me passing knots to the constructor gives this error, when it seems clear that the second "C/C++ prototype" should be satisfied.

I wish to emphasize that if I simply copy/paste the relevant code from common.h into HKnotVector.i I don't get this error.

I have two thoughts that might start someone down the right path:

  1. The error when I run swig on the .i file says nothing is known about the namespace util, which is where DoubleVec is typedefd in common.h
  2. I may (?) need to %include common.h somewhere so swig knows everything about it.

EDIT (5-11-13)

If I try to %include "common.h" in HKnotVector.i (just above the %include "../include/HKnotVector.h" line) the extension builds without the namespace error (HKnotVector.h:7: Error: Nothing known about namespace 'util'), but python cannot import it. This is what happens when I try to run test.py after building with the %include "common.h":

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    import hbspy
  File "/Users/spencerlyon2/Research/HBS/hbs/swig/hbspy.py", line 26, in <module>
    _hbspy = swig_import_helper()
  File "/Users/spencerlyon2/Research/HBS/hbs/swig/hbspy.py", line 22, in swig_import_helper
    _mod = imp.load_module('_hbspy', fp, pathname, description)
ImportError: dlopen(/Users/spencerlyon2/Research/HBS/hbs/swig/_hbspy.so, 2): Symbol not found: __ZN4utillsERSoRKNS_4AxisE
  Referenced from: /Users/spencerlyon2/Research/HBS/hbs/swig/_hbspy.so
  Expected in: dynamic lookup

I have no idea what __ZN4utillsERSoRKNS_4AxisE is, but I am assuming it is some internal symbol defined by the extension module or SWIG.


EDIT 2 (5-11-13)

If I go to common.h and remove the lines specifying the util namespace and comment out the using namespace util; line in HKnotVector.h everything works.

That is strange to me because the swig docs clearly state that "support for C++ namespaces is comprehensive..." and that " the default wrapping behavior is to flatten namespaces in the target language. This means that the contents of all namespaces are merged together in the resulting scripting language module." That seems to be the behavior I want, i.e. I would like to be python to be able to access everything from util as if it were in the global namespace.

In this small example removing namespace specifications is simple and straightforward. However, this is a small piece of a rather large project that already has deep use of namespaces. Although I got this example to compile, removing namespaces it not a viable long-term solution.

Upvotes: 0

Views: 1753

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 177901

As you've found, when SWIG processes header files it does not recurse into header files included by top-level header files by default, so you have to %include "common.h" specifically to expose its definitions to SWIG.

As you also found, it is bad practice to use using statements in headers files because they import everything into one namespace. In this case it confuses SWIG. Any .cpp file including a header with a using statement is forced to pollute its global namespace. Headers should respect namespace and specify them fully and leave using statements to .cpp files.

See Why is including "using namespace" into a header file a bad idea in C++?

Upvotes: 3

Related Questions