【问题标题】:Extend C# Proxy Class for a C++ template为 C++ 模板扩展 C# 代理类
【发布时间】:2016-09-16 14:29:07
【问题描述】:

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

假设我在 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.swigCMemOwntrue 还是false,所以我希望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 来完成此操作

目前我已经做到了:

  • 使用%csmethodmodifiers MyTemplateClass::Validate "private";Validate 设为私有
  • 通过%rename(InternalValidate, fullname=1) "MyTemplateClass::Validate";Validate 重命名为InternalValidate
  • 使用%typemap(cscode) 添加将调用InternalValidate 的新函数:

代码:

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

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

我尝试研究 SWIG 如何包装std::vector,似乎他们正在定义一个宏,然后以某种方式调用它来为向量的每个特化?我想我可以接受它,但我不喜欢它。

【问题讨论】:

    标签: c# c++ swig


    【解决方案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 行并在某处为 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>;
    

    因此,即使看起来应该可行的简单方法似乎行不通,仍然有很多选择可以实现我们需要的结果。

    【讨论】:

    • 感谢您的回答!我会看看什么最适合我。
    • 如果我采用%typemap(csout)的第一种方法,我可以说bool MyTemplateClass::Validate吗?我只想为这个类匹配那个函数(因为我用可能有多个 bool Validate 类型的函数包装了一个大型代码库)。
    • 回答我关于bool MyTemplateClass::Validate的问题,它似乎不匹配。
    • @AndyG 我不太确定为什么会这样 - 我以为它会起作用。假设您包含的标头被合理地分块,应该不会太难解决,您可能可以在正确的时间选择性地启用/禁用类型映射。
    猜你喜欢
    • 1970-01-01
    • 2011-07-08
    • 2011-05-19
    • 2020-05-29
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多