【问题标题】:SWIG: C++ to C#, pointer to pointer marshallingSWIG:C++ 到 C#,指向指针编组的指针
【发布时间】:2011-01-29 14:26:44
【问题描述】:

我有一些遗留代码想要移植到 C#。我无法修改 C++ 代码,我只需要使用我所提供的内容。

所以,情况。我正在使用 SwIG,我遇到了这个功能:

void MarshalMe(int iNum, FooClass** ioFooClassArray);

如果我对此运行 SWIG,它将不知道如何处理该数组,因此它将创建一个 SWIGTYPE_p_pFooClass。很公平! 用于此的 C# 代码看起来像

void MarshalMe(int iNum, SWIGTYPE_p_p_FooClass ioFooClassArray); // Not ideal!

有一些技术可以正确编组这种代码,所以我尝试了其中的一些:

%typemap(ctype)   FooClass** "FooClass**"
%typemap(cstype)  FooClass** "FooClass[]"
%typemap(imtype, inattributes="[In, Out, MarshalAs(UnmanagedType.LPArray)]") FooClass** "FooClass[]"
%typemap(csin)    FooClass** "$csinput"
%typemap(in)      FooClass** "$1 = $input;"
%typemap(freearg) FooClass** ""
%typemap(argout)  FooClass** ""

这有效地创建了一个更好的签名:

void MarshalMe(int iNum, FooClass[] ioFooClassArray); // Looks good! Would it work?

但是,当我尝试运行它时,我收到以下错误:

{"Exception of type 'System.ExecutionEngineException' was thrown."}

关于实际类型映射的任何想法?

【问题讨论】:

  • 我已经有一段时间没有接触 C++了,所以如果我在这里错了,请纠正我,但 FooClass** 不是暗示你正在处理二维对象吗? ** 指定维数。

标签: c# c++ pointers marshalling swig


【解决方案1】:

异常告诉您该函数已损坏垃圾收集堆。它写到数组的末尾。如果它实际上是一个 FooClass[],大问题,那么你必须先创建数组:

 FooClass[] array = new FooClass[666];
 MarshalMe(42, array);

假设函数将用 FooClass 对象填充数组。数组的大小在这里真的很重要,你必须知道你会得到多少元素。只有当“iNum”是一个表示数组有多长的参数时,这才能可靠地工作。传递array.Length。

这也可能意味着该函数将自己创建数组并返回一个指向它的指针。如果是这样的话,你就完蛋了,你不能释放数组的内存。

【讨论】:

    【解决方案2】:

    [Swig] Java:传递指针到指针的另一种方式

    1. 我在一个 C 函数 (API) 中遇到了类似的问题,它返回一个指向指针的指针作为输入参数。我试图从 JAVA 调用 C 函数,但无法修改 API。

    包含的 API.h 头文件:

    extern int ReadMessage(HEADER **hdr);
    

    原始的 C 调用如下所示:

    HEADER *hdr;
    int status;
    status = ReadMessage(&hdr);
    

    API 的功能是将数据存储在指针指定的内存位置。

    1. 我尝试使用 SWIG 创建适当的接口文件。 SWIG.i 从 API.h 创建了文件 SWIGTYPE_p_p_header.java。问题是 SWIGTYPE_p_p_header 构造函数将 swigCPtr 初始化为 0。

    JAVA 调用看起来像:

    SWIGTYPE_p_p_header hdr = new SWIGTYPE_p_p_header();
    status = SWIG.ReadMessage(hdr);
    

    但是当我从 JAVA 调用 API 时,ptr 始终为 0。

    1. 我终于放弃了将指针作为输入参数传递。相反,我在 SWIG.i 中定义了另一个 C 函数,以在返回值中返回指向指针的指针。我以为是 Kludge ……但它奏效了!

    你可能想试试这个:

    SWIG.i 看起来像:

    // return pointer-to-pointer
    %inline %{
       HEADER *ReadMessageHelper() {
       HEADER *hdr;
       int returnValue;
       returnValue = ReadMessage(&hdr);
       if (returnValue!= 1) hdr = NULL;
       return hdr;
    }%}
    
    1. 上面的内联函数可能会泄漏内存,因为 Java 不会获得 ReadMessageHelper 创建的内存的所有权,因为 HEADER 实例是在堆上创建的。

    内存泄漏的解决方法是将 ReadMessageHelper 定义为一个新对象,以便 Java 控制内存。

    %newobject ReadMessageHelper();
    
    JAVA call now would look like:
        HEADER hdr;
        hdr = SWIG.ReadMessageHelper();
    
    1. 如果您像我一样幸运的话,您可能有另一个 API 可用于释放消息缓冲区。在这种情况下,您不必执行第 4 步。

    2. SWIG 大师威廉·富尔顿 (William Fulton) 对上述方法有这样的看法:

    “我不认为辅助函数是一个杂物,更像是解决棘手问题的最简单解决方案。考虑 ReadMessage() 的等效纯 100% Java 代码。我不认为有一个等价的 Java 类是通过引用传递的,并且不存在对引用的引用或指向 Java 中指针的指针之类的东西。在您拥有的 C 函数中,一个 HEADER 实例由 ReadMessage 创建并传递回调用者。如果不提供一些围绕 HEADER 的包装类并将包装器传递给 ReadMessage 函数,我看不出如何在 Java 中做同样的事情。归根结底,ReadMessage 返回一个新创建的 HEADER,而 Java 返回新创建对象的方式是在返回值中返回它,而不是通过参数。”

    【讨论】:

      【解决方案3】:

      使用 SWIG 类型映射传递指针到指针:

      这是使用类型映射的另一种方法。它针对的是 Perl,而不是 Java,但概念是相同的。我终于设法使用类型映射而不使用辅助函数来让它工作:

      对于这个功能:

      typedef void *    MyType;
      int getblock( int a, int b, MyType *block );
      

      我有 2 个类型图:

      %typemap(perl5, in, numinputs=0) void ** data( void * scrap )
      {
          $1 = &scrap;
      }
      
      %typemap(perl5, argout) void ** data
      {
          SV* tempsv = sv_newmortal();
          if ( argvi >= items ) EXTEND(sp,1);
          SWIG_MakePtr( tempsv, (void *)*$1, $descriptor(void *), 0);
          $result = tempsv;
          argvi++;
      }
      

      而函数定义为:

      int getblock( int a, int b, void ** data ); 
      

      在我的 swig .i 文件中。现在,这会在 argout 类型映射中传回一个不透明的指针,因为这对这种特殊情况很有用,但是,如果您愿意,您可以将 SWIG_MakePtr 行替换为实际处理指针中的数据的内容。另外,当我想将指针传递给函数时,我有一个如下所示的类型映射:

      %typemap(perl5, in) void * data
      {
          if ( !(SvROK($input)) croak( "Not a reference...\n" );
      
          if ( SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0 ) == -1 )
              croak( "Couldn't convert $1 to $1_descriptor\n");
      }
      

      而函数定义为:

      int useblock( void * data );
      

      在我的 swig .i 文件中。

      显然,这都是 perl,但就类型映射架构而言,应该直接映射到 Java。希望对你有帮助...

      【讨论】:

        【解决方案4】:

        我设法使用Swig Managed Arrays and Pinning 文档解决了这个问题。

        给定一个 C++ 函数

        void myArrayCopy(int *sourceArray, int *targetArray, int nitems);

        在 C# 中将方法声明为不安全

        %csmethodmodifiers myArrayCopy "public unsafe";

        添加适当的类型映射

        %include "arrays_csharp.i"
        %apply int FIXED[] {int *sourceArray} 
        %apply int FIXED[] {int *targetArray}
        

        结果,我们在模块类中得到如下方法:

        public unsafe static void myArrayCopy(int[] sourceArray, int[] targetArray, int nitems) 
        {
            fixed ( int *swig_ptrTo_sourceArray = sourceArray ) 
            {
                fixed ( int *swig_ptrTo_targetArray = targetArray ) 
                {
                    examplePINVOKE.myArrayCopy((IntPtr)swig_ptrTo_sourceArray,            
        
                        (IntPtr)swig_ptrTo_targetArray,
                        nitems);
                }
            }
        }
        

        实际上,这可能与 FooClass** 略有不同,但 Swig 确实支持指向指针编组的直接指针,这也避免了复制,因此可以认为性能更好

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-25
          • 2013-04-19
          • 1970-01-01
          • 2023-03-11
          • 2021-12-31
          • 1970-01-01
          • 2013-07-11
          相关资源
          最近更新 更多