swig:扩展类模板以提供__str__
假设你有一个模板类 Foo
,你想用 Swig 来包装它,这样你就可以直接打印这个类的内容。
>>> from example import *
>>> f = Foo2()
>>> print(f)
In Foo class!
#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;
}
};
而我的接口文件是:
%{
#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();
}
};
这样做是没问题的,我可以像以前一样打印对象,但我想让这个功能适用于任何模板参数的值,因为为每个模板参数复制代码是没有意义的。我在接口文件中尝试了以下内容,但没有成功:
template <int d> class Foo {
public:
%extend {
const char *__str__() {
std::ostringstream oss(std::ostringstream::out);
oss << *self;
return oss.str().c_str();
}
}
};
2 个回答
1
你在最后一个例子中使用的 %extend
语法应该是正确的,这是一种我们在 OpenStudio 中使用的技巧。
我觉得问题在于你定义模板的地方有点重复,你在 %import
指令中定义了一次,在 .i
文件中又定义了一次。SWIG 实际上使用的是第一次定义的内容。
虽然这样不是最理想的情况,但我认为你需要去掉 %include "foo.hpp"
指令,明确地定义你想要的接口。你新的 .i
文件现在应该像这样:
%{
#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>;
另外,你也可以直接把 SWIG 代码放在你的 hpp 文件里,这样就不需要维护两个 API 了:
新的 .i 文件:
%{
#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>;
新的 .hpp 文件:
#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;
}
};
3
你可以通过省略模板参数列表,从模板的定义外部来%extend
主模板:
%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>;
或者你可以使用一个SWIG宏,一次性包裹并扩展每个特化:
%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)
请注意,在这两种情况下,你都会导致未定义的行为,因为你返回了一个在函数返回之前就被销毁的std::string
的.c_str()
结果。