当您延长课程时%extend
您实际上不能添加任何新的成员变量,只能添加成员函数。如果您在以下位置执行此操作,它将假定存在 get/set 函数%extend
.
原因是新成员变量的存储(即内存)必须位于某个地方,但没有明显的安全位置来放置它。不能修改原来的C++class
,因为如果这样做,当其他一些预编译代码使用类的旧定义时,您最终会得到未定义的行为;无法追溯应用新定义。您不能将其放入以目标语言生成的代理中,因为 C++ 类的实例与目标语言中的代理之间不存在 1:1 映射。 (考虑两个函数都通过指针返回相同的全局实例,以了解如何发生这种情况的简单示例)。
如果您想实现 get/set 函数来做一些有用的事情(例如,这里我使用全局映射来存储额外的数据),您不想在内部实现它们%extend
例如,给定 test.hh 仅包含:
template <typename T>
struct Foo {};
您可以使用std::map
通过在包装器中编写自己的获取和设置作为自由函数来完成您想要做的事情 -%{ %}
只需直接传递代码即可:
%module test
%include "test.hh"
%{
#include "test.hh"
%}
%{
#include <map>
static std::map<Foo<int>*, double> extra_stuff;
const double& Foo_Sl_int_Sg__x_get(Foo<int>* f) {
return extra_stuff[f];
}
void Foo_Sl_int_Sg__x_set(Foo<int>* f, const double& d) {
extra_stuff[f] = d;
}
%}
%template(FooInt) Foo<int>;
%extend Foo<int> {
double x;
}
get/set 函数被 SWIG 自己的损坏系统损坏,因为它是一个模板。我只是查看生成的包装类以了解它们的名称。 (我认为可能有一种更聪明的方法来做到这一点,但我还无法从文档中弄清楚)。如果它不是模板,函数的名称会简单得多。
请注意,这里没有任何规定可以从此地图中删除条目 - 它只会无限期地增长。 (例如,您可以通过提供一个类型映射来解决这个问题,该类型映射在对象生命周期结束时调用额外的函数)。您还需要小心线程。
由于您没有指定您使用的语言,我用 Java 测试了上面的代码:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
FooInt f = new FooInt();
f.setX(0.1);
System.out.println(f.getX());
}
}
或者,如果您确实愿意,您可以编写代码来确保目标语言中有唯一的单个代理实例,方法是编写一个类型映射来检查是否已创建代理而不是总是创建代理,但这会引发引用问题计数和线程安全很难有效且通用地解决,这就是为什么它不是默认行为。
在很多情况下(例如目标语言中的迭代器),最简单的解决方法是使用%inline
立即声明、定义和包装一个全新的额外类,该类提供您想要的功能。