2.1.6 C++和C#数据传递 ----vectorofvectorofInt
1 C++ 动态库输出设定
1.1 设定一个BD_API宏
Windows环境下,动态库导出,需要指定extern “C” __declspec(dllexport) 修饰符
Windows环境下,动态库导入,需要指定extern “C” __declspec(dllimport) 修饰符
Linux环境下,无需指定修饰符
以下代码可以满足跨平台编译
#pragma once
#ifdef _WIN32
#ifdef BD_STATIC// 静态库
#define BAPI_EXPORTS
#else // 动态库
#ifdef BD_SHARED // 导入库
#define BAPI_EXPORTS __declspec(dllexport)
#else // 输出库
#define BAPI_EXPORTS __declspec(dllimport)
#endif // EXPORT_DLL_IMPORT
#endif // DEBUG
#else
#define BAPI_EXPORTS
#endif
#define BD_API extern "C" BAPI_EXPORTS
#define BD_API_CPLUS BAPI_EXPORTS
/**
*
* @brief
* 如果你在使用Opencv, uchar 是OpenCV 的头文件中被定义过的
* 如果没有使用Opencv,需要自己定义uchar
*/
typedef unsigned char uchar;
1.2 动态链接库头文件设定 dllexport_vectorvectorint.h
主要是通过指针进行数据的传递和 类型的创建
#pragma once
#include <bd_config.h>
#include <vector>
/// @brief 在头文件中 尽量不要用using namespace std; 因为有可能和其他头文件中 有些变量或者Enum 同名导致异常
namespace TEST_EXPORT
{
/// @brief vector_vector_int_new1 创建2维vectorvectorint
/// @param values
/// @param size1 sizeof vector<int>
/// @param size2 每个vector<int> 的element个数
/// @return
BD_API std::vector<std::vector<int>>* vector_vector_int_new1(int **values, int size1, int *size2);
/// @brief vector_vector_int_new
/// @param size
/// @return
BD_API std::vector<std::vector<int>>* vector_vector_int_new(int size);
/// @brief vector_vector_int_getSize1
/// @param ptr
/// @return 返回vector<int>的 数量
BD_API int vector_vector_int_getSize1(std::vector<std::vector<int> >* ptr);
/// @brief vector_vector_int_getSize2
/// @param vec
/// @param sizes
/// @return 返回数组size[], 每个vector<int>的size
BD_API void vector_vector_int_getSize2(std::vector<std::vector<int> >* vec, size_t *sizes);
/// @brief vector_vector_int_getPointer
/// @param vec
/// @return 返回第一个元素 vector<int>的指针
BD_API std::vector<int>* vector_vector_int_getPointer(std::vector<std::vector<int> >* vec);
/// @brief vector_vector_int_getvalues
/// @param vec
/// @param dst dst 必须预先分配好内存,int[size1][]
/// @return
BD_API void vector_vector_int_getvalues(std::vector<std::vector<int> > *vec, int **dst);
/// @brief delete_vector_vector_int
/// @param ptr
/// @return
BD_API void delete_vector_vector_int(std::vector<std::vector<int>> * ptr);
}
1.3 动态链接库函数实现 dllexport_vectorvectorint.cpp
二维数据传递,关键是传递一个 一维的地址数组
#include "dllexport_vectorvectorint.h"
#include<string.h>
#include <iostream>
#include <math.h>
#include <algorithm>
//stp1 vectorshort
using namespace std;
namespace TEST_EXPORT
{
/// @brief vector_vector_int_new1 创建2维vectorvectorint
/// @param values
/// @param size1 sizeof vector<int>
/// @param size2 每个vector<int> 的element个数
/// @return
BD_API std::vector<std::vector<int>>* vector_vector_int_new1(int **values, int size1, int *size2)
{
if(values==nullptr||size1==0)return nullptr ;
std::vector<std::vector<int> > *vec = new std::vector<std::vector<int> >(size1);
for (int i = 0; i < size1; i++)
{
vec->at(i) = std::vector<int>(values[i], values[i] + size2[i]);
}
return vec;
}
/// @brief vector_vector_int_new
/// @param size
/// @return
BD_API std::vector<std::vector<int>>* vector_vector_int_new(int size)
{
std::vector<std::vector<int> > *vec = new std::vector<std::vector<int> >(size);
return vec;
}
/// @brief vector_vector_int_getSize1
/// @param ptr
/// @return 返回vector<int>的 数量
BD_API int vector_vector_int_getSize1(std::vector<std::vector<int> >* ptr)
{
if (ptr == nullptr)return 0;
int size = static_cast<int>(ptr->size());
return size;
}
/// @brief vector_vector_int_getSize2
/// @param vec
/// @param sizes 需要先分配好内存 >=size1
/// @return 返回数组size[], 每个vector<int>的size
BD_API void vector_vector_int_getSize2(std::vector<std::vector<int> >* vec, size_t *sizes)
{
if (vec == nullptr)return;
for (size_t i = 0; i < vec->size(); i++)
{
sizes[i] = vec->at(i).size();
}
}
/// @brief vector_vector_int_getPointer
/// @param vec
/// @return 返回第一个元素 vector<int>的指针
BD_API std::vector<int>* vector_vector_int_getPointer(std::vector<std::vector<int> >* vec)
{
if(vec->size()>0)
return &(vec->at(0));
else return nullptr;
}
/// @brief vector_vector_int_getvalues
/// @param vec
/// @param dst dst 必须预先分配好内存,int[size1][]
/// @return
BD_API void vector_vector_int_getvalues(std::vector<std::vector<int> > *vec, int **dst)
{
if (vec == nullptr||dst==nullptr)return;
for (size_t i = 0; i < vec->size(); ++i)
{
const auto& srcI = vec->at(i);
const auto dstI = dst[i];
for (size_t j = 0; j < srcI.size(); ++j)
{
dstI[j] = srcI[j];
}
}
}
/// @brief delete_vector_vector_int
/// @param ptr
/// @return
BD_API void delete_vector_vector_int(std::vector<std::vector<int>> * ptr)
{
if (ptr == nullptr)return;
delete ptr;
}
}
2 C#中动态库导入
- 定义一个抽象类 public abstract class DisposableObject : IDisposable
- 定义抽象类 public abstract class DisposableCvObject : DisposableObject
- 定义类 ArrayAddress2.cs 用于转换二维数组 到一维地址指针数组的转换
- 定义pInvoker.cs 用于导入 c++ dll
- 定义封装类wraper.cs ,用于封装一定对应于C++ 的类,管理创建,赋值,销毁
2.1 DisposableObject 类声明 ,请参考OpencvSharp
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace OpenCvSharp
{
/// <summary>
/// Represents a class which manages its own memory.
/// </summary>
public abstract class DisposableObject : IDisposable
{
/// <summary>
/// Gets or sets a handle which allocates using cvSetData.
/// </summary>
protected GCHandle dataHandle;
private volatile int disposeSignaled = 0;
#region Properties
/// <summary>
/// Gets a value indicating whether this instance has been disposed.
/// </summary>
public bool IsDisposed { get; protected set; }
/// <summary>
/// Gets or sets a value indicating whether you permit disposing this instance.
/// </summary>
public bool IsEnabledDispose { get; set; }
/// <summary>
/// Gets or sets a memory address allocated by AllocMemory.
/// </summary>
protected IntPtr AllocatedMemory { get; set; }
/// <summary>
/// Gets or sets the byte length of the allocated memory
/// </summary>
protected long AllocatedMemorySize { get; set; }
#endregion
#region Init and Dispossal
/// <summary>
/// Default constructor
/// </summary>
protected DisposableObject()
: this(true)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="isEnabledDispose">true if you permit disposing this class by GC</param>
protected DisposableObject(bool isEnabledDispose)
{
IsDisposed = false;
IsEnabledDispose = isEnabledDispose;
AllocatedMemory = IntPtr.Zero;
AllocatedMemorySize = 0;
}
/// <summary>
/// Releases the resources
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the resources
/// </summary>
/// <param name="disposing">
/// If disposing equals true, the method has been called directly or indirectly by a user's code. Managed and unmanaged resources can be disposed.
/// If false, the method has been called by the runtime from inside the finalizer and you should not reference other objects. Only unmanaged resources can be disposed.
/// </param>
private void Dispose(bool disposing)
{
#pragma warning disable 420
// http://stackoverflow.com/questions/425132/a-reference-to-a-volatile-field-will-not-be-treated-as-volatile-implications
if (Interlocked.Exchange(ref disposeSignaled, 1) != 0)
{
return;
}
IsDisposed = true;
if (IsEnabledDispose)
{
if (disposing)
{
DisposeManaged();
}
DisposeUnmanaged();
}
}
/// <summary>
/// Destructor
/// </summary>
~DisposableObject()
{
Dispose(false);
}
/// <summary>
/// Releases managed resources
/// </summary>
protected virtual void DisposeManaged()
{
}
/// <summary>
/// Releases unmanaged resources
/// </summary>
protected virtual void DisposeUnmanaged()
{
if (dataHandle.IsAllocated)
{
dataHandle.Free();
}
if (AllocatedMemorySize > 0)
{
GC.RemoveMemoryPressure(AllocatedMemorySize);
AllocatedMemorySize = 0;
}
if (AllocatedMemory != IntPtr.Zero)
{
Marshal.FreeHGlobal(AllocatedMemory);
AllocatedMemory = IntPtr.Zero;
}
}
#endregion
#region Methods
/// <summary>
/// Pins the object to be allocated by cvSetData.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
protected internal GCHandle AllocGCHandle(object obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
if (dataHandle.IsAllocated)
dataHandle.Free();
dataHandle = GCHandle.Alloc(obj, GCHandleType.Pinned);
return dataHandle;
}
/// <summary>
/// Allocates the specified size of memory.
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
protected IntPtr AllocMemory(int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
if (AllocatedMemory != IntPtr.Zero)
Marshal.FreeHGlobal(AllocatedMemory);
AllocatedMemory = Marshal.AllocHGlobal(size);
NotifyMemoryPressure(size);
return AllocatedMemory;
}
/// <summary>
/// Notifies the allocated size of memory.
/// </summary>
/// <param name="size"></param>
protected void NotifyMemoryPressure(long size)
{
// マルチスレッド動作時にロックがかかるらしい。いったん廃止
if (!IsEnabledDispose)
return;
if (size == 0)
return;
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
if (AllocatedMemorySize > 0)
GC.RemoveMemoryPressure(AllocatedMemorySize);
AllocatedMemorySize = size;
GC.AddMemoryPressure(size);
}
/// <summary>
/// If this object is disposed, then ObjectDisposedException is thrown.
/// </summary>
public void ThrowIfDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
#endregion
}
}
2.2 disposableobject.cs类声明 ,请参考OpencvSharp
用于管理销毁 非托管的 指针
using System;
namespace OpenCvSharp
{ /// <summary>
/// DisposableObject + ICvPtrHolder
/// </summary>
public abstract class DisposableCvObject : DisposableObject
{
/// <summary>
/// Data pointer
/// </summary>
protected IntPtr ptr;
#region Init and Dispose
/// <summary>
/// Default constructor
/// </summary>
protected DisposableCvObject()
: this(true)
{
}
protected DisposableCvObject(IntPtr ptr)
: this(ptr, true)
{
}
/// <summary>
///
/// </summary>
/// <param name="isEnabledDispose"></param>
protected DisposableCvObject(bool isEnabledDispose)
: this(IntPtr.Zero, isEnabledDispose)
{
}
/// <summary>
///
/// </summary>
/// <param name="ptr"></param>
/// <param name="isEnabledDispose"></param>
protected DisposableCvObject(IntPtr ptr, bool isEnabledDispose)
: base(isEnabledDispose)
{
this.ptr = ptr;
}
/// <summary>
/// releases unmanaged resources
/// </summary>
protected override void DisposeUnmanaged()
{
ptr = IntPtr.Zero;
base.DisposeUnmanaged();
}
#endregion
/// <summary>
/// Native pointer of OpenCV structure
/// </summary>
public IntPtr CvPtr
{
get
{
ThrowIfDisposed();
return ptr;
}
}
}
}
2.3 辅助函数ArrayAddress2.cs ,实现二维数组到地址指针数组的转换
参考OpenCvSharp\Util\ArrayAddress2.cs
//ref:OpenCvSharp\Util\ArrayAddress2.cs
//目的: 把二维数组转换为 可以传递给 C++ 的一维地址指针,同时保存每一维的length
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace OpenCvSharp.Util
{
/// <summary>
/// Class to get address of specified jagged array
/// </summary>
/// <typeparam name="T"></typeparam>
public class ArrayAddress2<T> : DisposableObject
where T : struct
{
#pragma warning disable 1591
protected T[][] array;
protected GCHandle[] gch;
protected IntPtr[] ptr;
protected object original;
/// <summary>
///
/// </summary>
public ArrayAddress2()
{
}
/// <summary>
///
/// </summary>
/// <param name="array"></param>
public ArrayAddress2(T[][] array)
{
Initialize(array);
}
/// <summary>
///
/// </summary>
/// <param name="enumerable"></param>
public ArrayAddress2(IEnumerable<IEnumerable<T>> enumerable)
{
if (enumerable == null)
throw new ArgumentNullException(nameof(enumerable));
original = enumerable;
var list = new List<T[]>();
foreach (IEnumerable<T> e in enumerable)
{
if (e == null)
throw new ArgumentException("enumerable contains null");
list.Add(new List<T>(e).ToArray());
}
Initialize(list.ToArray());
}
protected void Initialize(T[][] target)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
array = target;
// T[][]をIntPtr[]に変換する
ptr = new IntPtr[array.Length];
gch = new GCHandle[array.Length];
for (int i = 0; i < array.Length; i++)
{
T[] elem = array[i];
if (elem == null/* || elem.Length == 0*/)
{
throw new ArgumentException(string.Format("array[{0}] is not valid array object.", i));
}
// メモリ確保
gch[i] = GCHandle.Alloc(elem, GCHandleType.Pinned);
ptr[i] = gch[i].AddrOfPinnedObject();
}
}
/// <summary>
/// Releases unmanaged resources
/// </summary>
protected override void DisposeUnmanaged()
{
foreach (GCHandle h in gch)
{
if (h.IsAllocated)
{
h.Free();
}
}
base.DisposeUnmanaged();
}
#if LANG_JP
/// <summary>
/// ポインタを得る
/// </summary>
/// <returns></returns>
#else
/// <summary>
///
/// </summary>
#endif
public IntPtr[] Pointer
{
get { return ptr; }
}
#if LANG_JP
/// <summary>
/// ポインタへの暗黙のキャスト
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
#else
/// <summary>
///
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
#endif
public static implicit operator IntPtr[] (ArrayAddress2<T> self)
{
return self.Pointer;
}
/// <summary>
///
/// </summary>
public int Dim1Length
{
get { return array.Length; }
}
/// <summary>
///
/// </summary>
public int[] Dim2Lengths
{
get
{
var lengths = new int[array.Length];
for (int i = 0; i < array.Length; i++)
{
lengths[i] = array[i].Length;
}
return lengths;
}
}
}
}
2.4 通过DllImport 申明C++ dll 中的函数原型 文件pinvoke.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace dllimport_vectorvectorint
{
public static partial class Nativemethods_bd
{
public const string DllExtern = "dllexport_vectorvectorintd.dll";
public const string Version = "400";
// create a new region_blob ptr
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief vector_vector_int_new1 创建2维vectorvectorint
/// @param values
/// @param size1 sizeof vector<int>
/// @param size2 每个vector<int> 的element个数
/// @return
public static extern IntPtr vector_vector_int_new1(IntPtr[] values, int size1, int[] size2);
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief vector_vector_int_new
/// @param size
/// @return
public static extern IntPtr vector_vector_int_new(int size);
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief vector_vector_int_getSize1
/// @param ptr
/// @return 返回vector<int>的 数量
public static extern int vector_vector_int_getSize1(IntPtr ptr);
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief vector_vector_int_getSize2
/// @param vec
/// @param sizes IntPtr 对应于 c++ size_t
/// @return 返回数组size[], 每个vector<int>的size
public static extern void vector_vector_int_getSize2(IntPtr ptr, IntPtr[] sizes);
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief vector_vector_int_getPointer
/// @param vec
/// @return 返回第一个元素 vector<int>的指针
public static extern IntPtr vector_vector_int_getPointer(IntPtr vec);
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief vector_vector_int_getvalues
/// @param vec
/// @param dst dst 必须预先分配好内存,二维数组 对应关系int[size1][] -->IntPtr[]
/// @return
public static extern void vector_vector_int_getvalues(IntPtr ptr, IntPtr[] dst);
[DllImport(DllExtern, ExactSpelling = true)]
/// @brief delete_vector_vector_int
/// @param ptr
/// @return
public static extern void delete_vector_vector_int(IntPtr ptr);
}
}
2.5 wrapper.cs 封装对应C++ 中类,进行非托管的内存的管理,赋值,销毁
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.Util;
namespace dllimport_vectorvectorint
{
/// <summary>
///
/// </summary>
public class VectorOfVectorInt : DisposableCvObject
{
/// <summary>
///
/// </summary>
public VectorOfVectorInt()
{
ptr = Nativemethods_bd.vector_vector_int_new(0);
}
public VectorOfVectorInt(IntPtr ptr)
{
this.ptr = ptr;
}
/// <summary>
///
/// </summary>
/// <param name="size"></param>
public VectorOfVectorInt(int size)
{
if (size < 0)
throw new ArgumentOutOfRangeException(nameof(size));
ptr = Nativemethods_bd.vector_vector_int_new(size);
}
/// <summary>
///
/// </summary>
/// <param name="values"></param>
public VectorOfVectorInt(int[][] values)
{
if (values == null)
throw new ArgumentNullException(nameof(values));
// 使用 ArrayAddress2 生成 IntPtr[size1]数组,进行数据的传递
using (var aa = new ArrayAddress2<int>(values))
{
ptr = Nativemethods_bd.vector_vector_int_new1(
aa.Pointer, aa.Dim1Length, aa.Dim2Lengths);
}
}
public VectorOfVectorInt(List<List<int>> values)
{
if (values == null)
throw new ArgumentNullException(nameof(values));
using (var aa = new ArrayAddress2<int>(values))
{
ptr = Nativemethods_bd.vector_vector_int_new1(
aa.Pointer, aa.Dim1Length, aa.Dim2Lengths);
}
}
/// <summary>
/// Releases unmanaged resources
/// </summary>
protected override void DisposeUnmanaged()
{
Nativemethods_bd.delete_vector_vector_int(ptr);
base.DisposeUnmanaged();
}
/// <summary>
/// vector.size()
/// </summary>
public int Size1
{
get
{
var res = Nativemethods_bd.vector_vector_int_getSize1(ptr);
GC.KeepAlive(this);
return res;
}
}
/// <summary>
///
/// </summary>
public int Size
{
get { return Size1; }
}
/// <summary>
/// vector[i].size()
/// </summary>
public long[] Size2
{
get
{
int size1 = Size1;
// C# IntPtr 对应于C++ size_t
IntPtr[] size2Org = new IntPtr[size1];
Nativemethods_bd.vector_vector_int_getSize2(ptr, size2Org);
GC.KeepAlive(this);
long[] size2 = new long[size1];
for (int i = 0; i < size1; i++)
{
size2[i] = size2Org[i].ToInt64();
}
return size2;
}
}
/// <summary>
/// &vector[0]
/// </summary>
public IntPtr ElemPtr
{
get
{
var res = Nativemethods_bd.vector_vector_int_getPointer(ptr);
GC.KeepAlive(this);
return res;
}
}
/// <summary>
/// Converts std::vector to managed array
/// </summary>
/// <returns></returns>
public int[][] ToArray()
{
if (ptr == null) return null;
int size1 = Size1;
if (size1 == 0)
return new int[0][];
long[] size2 = Size2;
var ret = new int[size1][];
for (int i = 0; i < size1; i++)
{
ret[i] = new int[size2[i]];
}
//使用 GCHandle.Alloc(elem, GCHandleType.Pinned); 把数组内存 pin住,防止在copy过程中C# 数组被删除
using (var retPtr = new ArrayAddress2<int>(ret))
{
Nativemethods_bd.vector_vector_int_getvalues(ptr, retPtr);
GC.KeepAlive(this);
}
return ret;
}
public List<List<int>> ToList()
{
if (ptr == null) return null;
int[][] array = this.ToArray();
List<List<int>> curves = new List<List<int>>();
if (array != null)
{
int length = array.Length;
for (int i = 0; i < length; i++)
{
if (array[i] != null)
{
List<int> contour = new List<int>(array[i]);
curves.Add(contour);
Array.Clear(array[i], 0, array[i].Length);
}
}
Array.Clear(array, 0, array.Length);
}
return curves;
}
public static void Array_Clear(int[][] array)
{
if (array == null) return;
int size1 = array.Length;
for (int i = 0; i < size1; i++)
{
if (array[i] == null) break;
Array.Clear(array[i], 0, array[i].Length);
}
Array.Clear(array, 0, size1);
return;
}
}
}
2.6 main 函数中调用函数 文件 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace dllimport_vectorvectorint
{
class Program
{
static void Main(string[] args)
{
//定义一个 List<List<int>> 类型变量
List<List<int>> vec_ints = new List<List<int>>();
List<int> data1 = new List<int>() { 1,3,4};
List<int> data2 = new List<int>() { 2, 4, 6,7 };
vec_ints.Add(data1);
vec_ints.Add(data2);
// 传递进入C++
using (var var = new VectorOfVectorInt(vec_ints))
{
// 读取C++ 的数据,并且显示
int size1 = var.Size1;
long[] size2 = var.Size2;
// 数据处理,后续如果需要向C++传递数据, 只要传递 VectorOfVectorInt 的指针即可 例如 var.CvPtr;
int[][] array = var.ToArray();// 内部使用了 var.CvPtr,进行数据的交互
// 显示结果
for(int i=0;i<size1;i++)
{
Console.Write("vectorvectorint import ,data{0}=", i);
for (int j = 0; j < size2[i]; j++)
{
Console.Write("{0},", array[i][j]);
}
Console.WriteLine("\t");
}
}
Console.ReadKey();
}
}
}
C# 运行后控制台结果显示
vectorvectorint import ,data0=1,3,4,
vectorvectorint import ,data1=2,4,6,7,