gpt4 book ai didi

c# - 为 C++ 模板扩展 C# 代理类

转载 作者:行者123 更新时间:2023-11-30 05:22:38 24 4
gpt4 key购买 nike

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 设为私有(private);
  • 通过 %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?*/指定什么。我试过 T, T*, typemap(cstype, T) 但似乎没有一些特殊的 swig 变量,比如$csargtype 我可以用。

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

最佳答案

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

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(至少 3.0,来自 Ubuntu 14.04)在那个上下文中不知道任何关于 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>;

在上面的接口(interface)中,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 位分开。

但是,如果您不愿意像那样复制并且不能/不会简单地编辑头文件,那么所有内容都不会丢失。我们可以(滥用)使用 %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>;

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

关于c# - 为 C++ 模板扩展 C# 代理类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39533997/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com