Reputation: 2400
Say you have a template class Foo
, and you want to wrap it with Swig transparently so that you can print the class:
>>> from example import *
>>> f = Foo2()
>>> print(f)
In Foo class!
I have followed this post and this one. So my header file is:
#include <iostream>
template <int d> class Foo {
public:
friend std::ostream &operator<<(std::ostream &os, const Foo &m) {
os << "Inside Foo class!" << std::endl;
return os;
}
};
And my interface file:
%{
#include <sstream>
#include <iostream>
#include "foo.hpp"
%}
%include "std_iostream.i"
// Try grabbing it unmodified
%include "foo.hpp"
/* Instantiate a few different versions of the template */
%template(Foo2) Foo<2>;
%template(Foo3) Foo<3>;
%extend Foo<2> {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
};
So this works just fine, I can print the object as before, but I would like to generalize it for any value of the template parameter, as it doesn't make sense to copy that code for every template parameter. I tried the following in the interface file but it didn't work:
template <int d> class Foo {
public:
%extend {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
}
};
Upvotes: 4
Views: 1096
Reputation: 14714
You should be able to %extend
the primary template, from outside its definition, by omitting the template parameter list:
%extend Foo {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
};
%template(Foo2) Foo<2>;
%template(Foo3) Foo<3>;
Or you could use a SWIG macro to wrap and extend each specialization in one go:
%define WRAP_FOO(N)
%template( Foo ## N ) Foo<N>;
%extend Foo<N> {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
};
%enddef
/* Instantiate a few different versions of the template */
WRAP_FOO(2)
WRAP_FOO(3)
Note that in either case, you are causing undefined behaviour by returning the result of .c_str()
of a std::string
which is destroyed before the function returns.
Upvotes: 3
Reputation: 3416
The %extend
syntax you are using in your final example should be correct, this is a technique we are using in OpenStudio
I believe the problem is that you are defining the template 2 times, once in your %import
directive and once in your .i
file. The first definition is the one that SWIG is using.
While not ideal, I believe that you'll need to drop the %include "foo.hpp"
directive and define specifically the interface you want. Your new .i
file becomes now something like:
%{
#include <sstream>
#include <iostream>
#include "foo.hpp"
%}
%include "std_iostream.i"
// Don't let SWIG directly parse foo.hpp
// %include "foo.hpp"
template <int d> class Foo {
public:
// include here prototypes for all functions
// you want exposed. You don't need the implementation like in
// a normal C++ template declaration
// include here any extensions you want to add
%extend {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
}
};
/* Instantiate a few different versions of the template */
%template(Foo2) Foo<2>;
%template(Foo3) Foo<3>;
Alternatively, you could place the SWIG code in your hpp file directly and save having to maintain two APIs:
New .i file:
%{
#include <sstream>
#include <iostream>
#include "foo.hpp"
%}
%include "std_iostream.i"
// let swig directly parse foo.hpp
%include "foo.hpp"
/* Instantiate a few different versions of the template */
%template(Foo2) Foo<2>;
%template(Foo3) Foo<3>;
New .hpp file:
#include <iostream>
template <int d> class Foo {
public:
#ifdef SWIG
%extend {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
}
#endif
friend std::ostream &operator<<(std::ostream &os, const Foo &m) {
os << "Inside Foo class!" << std::endl;
return os;
}
};
Upvotes: 1