为了完成这些示例,我创建了以下头文件:
template<typename T>
struct MyTemplateClass
{
bool Validate(T* _in)
{
return false;
}
// ... other stuff... MyTemplateClass is also a container for T*
};
struct Foo {};
好消息是,生成您要求的代码实际上比您尝试的要简单得多。我们可以使用仅匹配 Validate 的 csout 类型映射,我们就可以开始了:
%module test
%{
#include "test.hh"
%}
%typemap(csout, excode=SWIGEXCODE) bool Validate {
// referring to _in by name is a bit of a hack here, but it works...
bool ret = _in.swigCMemOwn && $imcall;$excode
return ret;
}
%include "test.hh"
%template(MyTemplateClassInt) MyTemplateClass<int>;
%template(MyTemplateClassFoo) MyTemplateClass<Foo>;
为了完整起见,让我们看看提出的原始问题。首先,让我们通过使 MyTemplateClass 实际上不是模板来简化事情(即注释掉 test.hh 的第 1 行并在某处为 T 添加 typedef)。
在那种情况下,您尝试做的工作几乎可以完成,使用 $typemap(cstype, T) 在 SWIG 编译时查找用于给定类型的 C# 类型:
%module test
%{
#include "test.hh"
%}
%typemap(cscode) MyTemplateClass %{
public bool Validate($typemap(cstype, T) in) {
return in.swigCMemOwn && InternalValidate(in);
}
%}
%rename(InternalValidate) Validate;
%include "test.hh"
但是,当我们再次将其恢复为模板时,生成的代码不正确,生成的 Validate 是:
public bool Validate(SWIGTYPE_p_T in)
发生这种情况是因为 SWIG(至少在 Ubuntu 14.04 的 3.0 中)在该上下文中不知道有关 T 的任何信息 - 模板替换没有正确发生。我不太确定这是错误还是预期的行为,但不管怎样,这对我们来说都是个问题。
有趣的是,如果您愿意在 SWIG 认为替换确实有效的模板定义中编写 cscode 类型映射:
%module test
%{
#include "test.hh"
%}
%rename(InternalValidate) Validate;
template<typename T>
struct MyTemplateClass
{
bool Validate(T* _in)
{
return false;
}
// ... other stuff... MyTemplateClass is also a container for T*
%typemap(cscode) MyTemplateClass %{
public bool Validate($typemap(cstype, T) in) {
return in.swigCMemOwn && InternalValidate(in);
}
%}
};
struct Foo {};
%template(MyTemplateClassInt) MyTemplateClass<int>;
%template(MyTemplateClassFoo) MyTemplateClass<Foo>;
在上面的接口中,T 的类型确实被正确地替换到了输出中。因此,如果您愿意接受 .i 文件和您在库中使用的真实头文件之间的重复,那么这就足够了。您也可以编辑头文件本身并将 SWIG 和 C++ 混合到其中,以下修改后的 test.hh 达到相同的结果:
template<typename T>
struct MyTemplateClass
{
bool Validate(T* _in)
{
return false;
}
// ... other stuff... MyTemplateClass is also a container for T*
#ifdef SWIG
%typemap(cscode) MyTemplateClass %{
public bool Validate($typemap(cstype, T) in) {
return in.swigCMemOwn && InternalValidate(in);
}
%}
#endif
};
struct Foo {};
这很有效,因为 SWIG 定义了预处理器宏 SWIG,但在正常的 C++ 编译期间不会定义它,所以一切都很好。我个人不喜欢这样 - 我宁愿保持 C++ 和 SWIG 位在逻辑上用干净的边界分开。
但是,如果您不愿意像那样复制并且不能/不会简单地编辑头文件,那么一切都不会丢失。我们可以(ab)使用%extend 让我们做同样的事情:
%module test
%{
#include "test.hh"
%}
%rename(InternalValidate) Validate;
%include "test.hh"
%extend MyTemplateClass {
%typemap(cscode) MyTemplateClass %{
public bool Validate($typemap(cstype, T) in) {
return in.swigCMemOwn && InternalValidate(in);
}
%}
}
%template(MyTemplateClassInt) MyTemplateClass<int>;
%template(MyTemplateClassFoo) MyTemplateClass<Foo>;
这又奏效了。
最后一个解决方法是,如果您在模板中有一个只使用 T 的 typedef,例如:
template<typename T>
struct MyTemplateClass {
typedef T type;
//...
然后以下工作,将 typedef 引用为$1_basetype::type:
%module test
%{
#include "test.hh"
%}
%rename(InternalValidate) Validate;
%typemap(cscode) MyTemplateClass %{
public bool Validate($typemap(cstype, $1_basetype::type) in) {
return in.swigCMemOwn && InternalValidate(in);
}
%}
%include "test.hh"
%template(MyTemplateClassInt) MyTemplateClass<int>;
%template(MyTemplateClassFoo) MyTemplateClass<Foo>;
因此,即使看起来应该可行的简单方法似乎行不通,仍然有很多选择可以实现我们需要的结果。