SWIG:如何将 C++ 对象数组从 C# 传递到 C++?

Posted

技术标签:

【中文标题】SWIG:如何将 C++ 对象数组从 C# 传递到 C++?【英文标题】:SWIG: How do i pass an array of C++ objects from C# to C++? 【发布时间】:2017-04-25 12:14:24 【问题描述】:

我有一个 C++ 数据类,它存储指向某个堆分配内存的指针;我使用 SWIG 生成的包装器从 C# 实例化和使用它没有问题。 当我尝试调用包装库中需要这些数据类数组的函数时,我的问题就出现了;我每次都遇到分段错误。 我尝试了以下两种方法。

简而言之,我的情况是

class DataClass

   char* _data;
   ...

void printDataClassArray(DataClass *toPrint, std::size_t size)
 ... 

函数printDataClassArray 默认包装为void printDataClassArray(DataClass toPrint, int size),我尝试了两种不同的方法来克服这个问题。我在下面发布了完整的代码来帮助那些愿意帮助我的好心人:example.h 包含我要包装的 C++ 库,example.i 包含 SWIG 接口代码,consumer.cs 是一个消耗导出的 C# 函数函数和类。

我已经尝试对此进行研究,特别是我关注了这个帖子:Wrong values passed as parameter to C library using SWIG,但我没有找到答案。我不知道我是否遗漏了一些明显的东西,但我认为这应该是 SWIG 中示例的一部分,因为这是一种相当常见的情况。

我尝试了两种方法,即使用CSHARP_ARRAYS 和使用array_class,但都没有奏效。请参阅下方的 SWIG 代码以及下方的完整列表。

使用 _CSHARP_ARRAYS_

 CSHARP_ARRAYS(DataClass, DataClass);
 %apply DataClass INPUT[] DataClass *toprint 
 %inline %
 static void PrintAllDataArray(DataClass *toprint, std::size_t size)
 
     PrintAllData(toprint, size);
 
 %

使用 _array_class_

 // Methodology 2
 %array_class(DataClass, DataClassArray);
 %inline %
   static void PrintAllVariants(DataClassArray va, std::size_t size)
   
     PrintAllData(DataClassArray_cast(&va), size);
   
 %

example.h

#include <cstring> // for memcpy
#include <string> // for std::string
#include <sstream>
#include <iostream>
#include "assert.h"


inline char* copyString(const char* value, std::size_t size) 
  char* newstring = new char[size + 1];
  memcpy(newstring, value, size);
  newstring[size] = 0;
  std::cout << "Allocated string of " << size << " characters at " << reinterpret_cast<const void*>(newstring) << std::endl;
  return newstring;


struct DataEncapsulator 
    struct StringValue 
      const char* ptr;
      int size;
     svalue;
;

inline DataEncapsulator copyData(DataEncapsulator source) 
  DataEncapsulator copy = source;
    copy.svalue.ptr = copyString(source.svalue.ptr,
      source.svalue.size);
  return copy;


inline void deleteString(const char* string)

  std::printf("Deallocating string  at %p\n", reinterpret_cast<const void*>(string));
  delete[] string;


class DataClass 
  DataEncapsulator impl_;
public:
  DataClass()
  
    impl_.svalue.ptr = NULL;
    std::cout << "Empty constructor called on " << myAddress() << std::endl;
  

  DataClass(const char* string)
  
    std::cout << "Constructor called on " << myAddress() << std::endl;
    std::size_t len = std::strlen(string);
    impl_.svalue.ptr = copyString(string, len);
    impl_.svalue.size = len;
  

  DataClass(const DataClass& other) 
    std::cout << "Copy constructor, copying from " << other.myAddress() << " to " << myAddress() << std::endl;
    impl_ = copyData(other.impl_);
  

  ~DataClass()
  
    std::cout << "Destructor called on " << myAddress() << std::endl;
    deleteString(impl_.svalue.ptr);
  

  std::string myAddress()const
  
    std::ostringstream strs;
    strs << reinterpret_cast<const void*>(this);
    return strs.str();
  

  std::string toString() const
  
    std::ostringstream strs;
    strs << "\"" << impl_.svalue.ptr << "\"  - string address: " << reinterpret_cast<const void*>(impl_.svalue.ptr);
    return strs.str();
  

  const char* c_str() const
  
    return impl_.svalue.ptr;
  

  DataClass& operator=(const DataClass& other) 
    std::cout << "Assignment, copying from " << other.myAddress() <<  " to " << myAddress() << std::endl;
    deleteString(impl_.svalue.ptr);
    impl_ = copyData(other.impl_);
    return *this;
  
;

static void PrintAllData(DataClass toprint[], std::size_t size)

  std::cout << "PrintAllVariants passed array of " << size << " variants at " << reinterpret_cast<const void*>(toprint) << std::endl;
  if (size > 0)
  
    for (std::size_t i = 0; i < size; i++)
      std::cout <<  i << ": " << toprint[i].myAddress() << " - " << toprint[i].toString() << std::endl;
  

example.i

%module example
%include "std_string.i"
%include "arrays_csharp.i"
%include "carrays.i"

#ifdef _WIN32
%include <windows.i>
#endif

%inline %
  namespace std 
    typedef unsigned int size_t;
  
%
%ignore DataEncapsulator;
%ignore copyString;
%ignore copyData;
%ignore deleteString;

%
#include "example.h"
%
%rename(copy) *::operator=;
%include "example.h"


// Methodology 1
CSHARP_ARRAYS(DataClass, DataClass);
%apply DataClass INPUT[] DataClass *toprint 
%inline %
static void PrintAllDataArray(DataClass *toprint, std::size_t size)

  PrintAllData(toprint, size);

%

// Methodology 2
%array_class(DataClass, DataClassArray);
%inline %
  static void PrintAllVariants(DataClassArray va, std::size_t size)
  
    PrintAllData(DataClassArray_cast(&va), size);
  
%

consumer.cs

var a = new DataClass("first");
var b = new DataClass("second");
// Methodology 1
DataClassArray va = new DataClassArray(2);
va.setitem(0, a);
va.setitem(1, b);
example.PrintAllVariants(va, 2);


// Methodology 2
DataClass[] da = new DataClass[2];
da[0] = a;
da[1] = b;
example.PrintAllDataArray(da, 2);
Console.WriteLine();

【问题讨论】:

【参考方案1】:

经过大量调查,极端避免使用std::vector,朋友建议的一个非常简单的解决方案:将方法2中的辅助函数的签名更改为通过引用传递DataClassArray,所以来自:

static void PrintAllVariants(DataClassArray va, std::size_t size)

static void PrintAllVariants(DataClassArray &va, std::size_t size)

希望对你有帮助。

【讨论】:

以上是关于SWIG:如何将 C++ 对象数组从 C# 传递到 C++?的主要内容,如果未能解决你的问题,请参考以下文章

将字符串 (const char*) 从 C++ 传递到 C# 时,SWIG_csharp_string_callback 会导致内存泄漏

SWIG:如何将复数向量从 C++ 传递到 python

如何使用 swig 将 const 字符串参数从 perl 传递到 c++

如何将二维数组从 C# 传递到 C++?

如何将结构数组从 c++ 传递到 c#?

使用 SWIG 从 C++ 初始化对 C# 共享指针的引用