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 会导致内存泄漏