2016-09-16 53 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