当你扩展一个类时,%extend
你不能真正添加任何新的成员变量,只能添加成员函数。如果您在%extend
.
这样做的原因是新成员变量的存储(即内存)必须存在于某个地方,但是没有明显的安全位置可以放置它。您不能修改原始 C++ class
,因为如果您这样做,当其他一些预编译代码使用该类的旧定义时,您最终会出现未定义的行为;没有办法追溯应用新定义。您不能将它放在以目标语言生成的代理中,因为 C++ 类的实例和目标语言中的代理之间没有 1:1 的映射。(考虑两个函数,它们都通过指针返回相同的全局实例,以了解如何发生这种情况的简单示例)。
如果你想实现 get/set 函数来做一些有用的事情(例如,这里我使用全局映射来存储额外的数据)你不想在内部实现它们%extend
例如,给定 test.hh,只需要:
template <typename T>
struct Foo {};
您可以通过在包装器中编写自己的获取和设置作为自由函数来使用 astd::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
一次性声明、定义和包装一个全新的额外类,该类提供您想要的功能。