[英]SWIG: How do i pass an array of C++ objects from C# to C++?
I have a C++ data class which stores a pointer to some heap allocated memory; 我有一个C ++数据类,它存储指向某些堆分配的内存的指针。 I have no problem instantiating and consuming it from C# using a SWIG-generated wrapper.
我没有问题,可以使用SWIG生成的包装程序从C#实例化和使用它。 My problem arises when I try to call a function in the wrapped library which wants an array of those data classes;
当我尝试在包装的库中调用一个想要这些数据类数组的函数时,就会出现我的问题。 I hit segmentation faults every time.
我每次都遇到细分错误。 I have tried two approaches, listed below.
我尝试了以下两种方法。
In a nutshell, my situation is 简而言之,我的情况是
class DataClass
{
char* _data;
...
}
void printDataClassArray(DataClass *toPrint, std::size_t size)
{ ... }
The function printDataClassArray
gets wrapped by default as void printDataClassArray(DataClass toPrint, int size)
, I have tried two different approaches to overcome this. 默认情况下,函数
printDataClassArray
被包装为void printDataClassArray(DataClass toPrint, int size)
,我尝试了两种不同的方法来克服这一问题。 I post the full code below to assist the good soul who will try to help me: example.h
contains the C++ library I am trying to wrap, example.i
contains the SWIG interface code and consumer.cs
is a C# function consuming the exported function and classes. 我在下面发布了完整的代码以帮助善良的灵魂,他们将尽力帮助我:
example.h
包含我要包装的C ++库, example.i
包含SWIG接口代码, consumer.cs
是使用导出的C#函数功能和类。
I have tried researching this, in particular I have followed this thread: Wrong values passed as parameter to C library using SWIG but I found no answer. 我尝试研究此问题,尤其是遵循以下主题: 使用SWIG将错误的值作为参数传递给C库,但没有找到答案。 I don't know whether I am missing something obvious, but nevertheless I think this ought to be part of the examples in SWIG, as it's a fairly common situation.
我不知道我是否缺少明显的内容,但是我认为这应该是SWIG中示例的一部分,因为这是相当普遍的情况。
I have tried two approaches, namely using CSHARP_ARRAYS
and using array_class
, but none of them worked. 我尝试了两种方法,即使用
CSHARP_ARRAYS
和使用array_class
,但没有一种有效。 See the SWIG code just below and the complete listing further down. 请参阅下面的SWIG代码,以及完整的清单。
Using _CSHARP_ARRAYS_ 使用 _CSHARP_ARRAYS_
CSHARP_ARRAYS(DataClass, DataClass);
%apply DataClass INPUT[]{ DataClass *toprint }
%inline %{
static void PrintAllDataArray(DataClass *toprint, std::size_t size)
{
PrintAllData(toprint, size);
}
%}
Using _array_class_ 使用 _array_class_
// Methodology 2
%array_class(DataClass, DataClassArray);
%inline %{
static void PrintAllVariants(DataClassArray va, std::size_t size)
{
PrintAllData(DataClassArray_cast(&va), size);
}
%}
#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;
}
}
%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);
}
%}
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();
After a lot of investigation, going to extremes to avoid the use of std::vector
, an incredibly simple solution suggested by a friend: change the signature of the helper function in methodology 2 to pass the DataClassArray
by reference, so from: 经过大量调查,尽力避免使用
std::vector
,这是一个朋友建议的非常简单的解决方案:在方法2中更改辅助函数的签名,以通过引用传递DataClassArray
,因此:
static void PrintAllVariants(DataClassArray va, std::size_t size)
to 至
static void PrintAllVariants(DataClassArray &va, std::size_t size)
Hope it helps. 希望能帮助到你。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.