扩展C ++模板的C#代理类 [英] Extend C# Proxy Class for a C++ template

查看:127
本文介绍了扩展C ++模板的C#代理类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

TLDR: How do I access the template type "T" in C# for SWIG?

假设我有以下模板类在C ++中使用验证函数:

Let's say I have the following templated class in C++ with a Validate function:

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

假设我为各种对象实例化了类:

Let's say I instantiate the class for a variety of objects:

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

在C#中,我想要 函数也验证内存所有权。这是 _in.swigCMemOwn true 还是 false 所以我想C#包装看起来像这样( MyTemplateClassFoo

In C#, I want the Validate function to also validate memory ownership. That is whether _in.swigCMemOwn is true or false, so I'd like the C# wrapper to look something like this (for MyTemplateClassFoo)

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

这里是如果我想写我自己的验证函数,我不知道什么类型 _in 将要是。在Python中我可以用 feature(shadow) pythonprepend pythonappend

The problem here is that if I want to write my own Validate function, I don't know what type _in is going to be. In Python I could accomplish this with feature("shadow") or pythonprepend and pythonappend

现在我已经到达:


  • 使用验证私人使用 csmethodmodifiers MyTemplateClass :: Validateprivate;

  • 通过重命名 InternalValidate %rename(InternalValidate,fullname = 1) MyTemplateClass :: Validate;

  • 使用%typemap(cscode)添加一个新函数将调用 InternalValidate

  • Make Validate private using %csmethodmodifiers MyTemplateClass::Validate "private";
  • Rename Validate to InternalValidate via %rename(InternalValidate, fullname=1) "MyTemplateClass::Validate";
  • Use %typemap(cscode) to add a new function that will call InternalValidate:

代码:

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

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

But I don't know what I should specify for /*Type?*/. I've tried T, T*, typemap(cstype, T) but it doesn't appear that there is some special swig variable like $csargtype I can use.

我试图研究如何SWIG包装 std :: vector ,它似乎可能是定义一个宏,然后以某种方式调用它为每个专业化向量?

I've tried looking into how SWIG wraps std::vector, and it appears that perhaps they are defining a macro, and then somehow calling it for every specialization of the vector? I guess I could live with that, but I wouldn't like it.

推荐答案

要完成这些示例,我创建了以下头文件:

To work through these examples I created the following header file:

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

struct Foo {};

你试过了。我们可以使用只匹配 Validate 的csout typemap,我们很好去:

The good news is that it's actually possible to generate the code you're asking for much more simply than what you've tried. We can just use a csout typemap that matches only for Validate and we're good to go:

%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)。


For completeness sake though let's look at the original question as posed. Firstly let's simplify things by making MyTemplateClass not actually be a template (i.e. comment out line 1 of test.hh and add a typedef for T somewhere instead).

在这个实例中,你试图做的事情几乎工作,使用 $ typemap(cstype,T)查找在SWIG编译时用于给定类型的C#类型:

In that instance what you tried to do does pretty much work, using $typemap(cstype, T) to lookup the C# type used for a given type at SWIG compile time:

%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 是:

However when we revert this back to being a template again the code generated isn't correct, the Validate that gets generated is:

public bool Validate(SWIGTYPE_p_T in)

最少与3.0,从Ubuntu 14.04)不知道任何关于T的上下文 - 模板替换没有正常发生。我不太确定这是一个错误或预期的行为,但是这两种方式对我们来说都是一个问题。

This is happening because SWIG (at least with 3.0, from Ubuntu 14.04) isn't aware of anything about T in that context - the template substitution isn't happening properly. I'm not quite sure if that's a bug or expected behaviour, but either way it's a problem for us.

有趣的是,如果你愿意写SWIG看到替换的模板定义中的cscode typemap会起作用:

What's interesting though is that if you're willing to write the cscode typemap inside the definition of the template that SWIG sees the substitution does work:

%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获得了相同的结果:

In the above interface the type for T does get substituted into the output correctly. So if you're willing to accept the duplication between the .i file and the real header files you use in the library then that's sufficient. You could also edit the header file itself and mix SWIG and C++ into that, the following modified test.hh achieves the same result:

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位逻辑上分开与一个干净的边界。

That works because SWIG defines the preprocessor macro SWIG, but it won't be defined during normal C++ compilation so all is good. Personally I don't like that - I'd rather keep the C++ and SWIG bits logically separated with a clean boundary.

如果你不愿意重复并且不能/不会简单地编辑头文件全部不丢失。我们可以(ab)使用%extend 让我们做同样的事情:

If however you're not willing to duplicate like that and can't/won't simply edit the header file all is not lost. We can (ab)use %extend to let us do the same thing:

%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,例如:

One final workaround is that if you've got a typedef inside the template that just uses T, e.g.:

template<typename T>
struct MyTemplateClass {
  typedef T type;
  //...

然后下面的工作,引用typedef作为 $ 1_basetype :: type

Then the following works, references the typedef as $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>;

所以,即使看起来像它应该工作的简单方法似乎还没有大量的选项打开,实现我们需要的结果。

So even though the simple way that looks like it ought to work doesn't seem to there are still plenty of options open that achieve the result we need.

这篇关于扩展C ++模板的C#代理类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆