2.1.6 C++和C#数据传递 ----vectorofvectorofInt传递

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#中动态库导入

  1. 定义一个抽象类 public abstract class DisposableObject : IDisposable
  2. 定义抽象类 public abstract class DisposableCvObject : DisposableObject
  3. 定义类 ArrayAddress2.cs 用于转换二维数组 到一维地址指针数组的转换
  4. 定义pInvoker.cs 用于导入 c++ dll
  5. 定义封装类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>
        /// &amp;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,
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

V言微语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值