2016-09-16 58 views
1

TLDR:如何在C#中为SWIG访问模板类型“T”?为C++模板扩展C#代理类

比方说,我在C++下面的模板类具有Validate功能:

template<typename T> 
struct MyTemplateClass 
{ 
    bool Validate(T* _in) 
    { 
     //... 
    } 
    // ... other stuff... MyTemplateClass is also a container for T* 
}; 

比方说,我实例化类的各种对象:

%template(MyTemplateClassFoo) MyTemplateClass<Foo> 
%template(MyTemplateClassBar) MyTemplateClass<Bar> 
// etc. 

在C#中,我想Validate函数也验证内存所有权。也就是说_in.swigCMemOwn是否truefalse,所以我想在C#包装器看起来像这样(为MyTemplateClassFoo

public class MyTemplateClassFoo{ 
    public bool Validate(Foo _in) { 
     bool ret = _in.swigCMemOwn && 
      ModuleCLRPINVOKE.MyTemplateClassFoo_Validate(swigcPtr, Foo.getCPtr(_in)); 
     // SWIGEXCODE stuff 
     return ret; 
    } 
// ... 
} 

这里的问题是,如果我想要写我自己的Validate功能,我不不知道_in将会是什么类型。在Python我可以feature("shadow")pythonprependpythonappend

做到这一点现在我就得到如下:

  • Validate私人使用%csmethodmodifiers MyTemplateClass::Validate "private";
  • 通过%rename(InternalValidate, fullname=1) "MyTemplateClass::Validate";
  • 用重命名ValidateInternalValidate%typemap(cscode)添加一个新功能,将调用InternalValidate

代码:

%typemap(cscode) MyTemplateClass %{ 
    public bool Validate(/*Type?*/ _in) 
    { 
     return _in.swigCMemOwn && InternalValidate(_in); 
    } 
%} 

但我不知道我应该指定/*Type?*/。我试过T,T*,typemap(cstype, T),但它似乎没有像$csargtype这样的特殊swig变量可以使用。

我试着研究SWIG如何包装std::vector,看起来也许他们正在定义一个宏,然后以某种方式调用它的矢量的每个专业化?我想我可以忍受,但我不喜欢它。

回答

1

要通过这些例子中工作,我创建了下面的头文件:

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行,而是在某处添加typedef)。

在这种情况下,你试图做的确实非常多的工作是什么,使用$typemap(cstype, T)查找在痛饮用于给定类型的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) 

这是发生,因为夜风(至少3.0,从Ubuntu的14.04)是不知道的大约T 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 {}; 

做是因为夜风定义了预处理宏痛饮,但它不会是在正常的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; 
    //... 

那么下面的作品,引用类型定义为$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>; 

所以,尽管看起来应该起作用的简单方式似乎还没有很多选项可以实现我们需要的结果。

+0

感谢您的回答!我会看到最适合我的东西。 – AndyG

+0

如果我采用'%typemap(csout)'的第一种方法,可以说'bool MyTemplateClass :: Validate'吗?我只想为该类匹配该函数(因为我用潜在的多个'bool Validate'函数包装了一个大型代码库)。 – AndyG

+0

要回答我关于'bool MyTemplateClass :: Validate'的问题,它看起来不匹配。 – AndyG