如果对pcl里的函数导出为动态库,分为以下几部分:
- 对c++动态库的导出;
- c#对c++动态库的加载;
- c#对第2步的调用
一、对c++动态库的导出
定义导出的宏定义:
#ifndef EXPORT
# define EXPORT(rettype) __declspec( dllexport ) rettype __cdecl
#endif
这里的rettype是占位符,用来表示函数的返回值
比如下面的代码就是将 D B S C A N K d t r e e C l u s t e r < p c l : : P o i n t X Y Z > ∗ DBSCANKdtreeCluster<pcl::PointXYZ>* DBSCANKdtreeCluster<pcl::PointXYZ>∗替代了占位符,其实就是在使用的时候用实际的类型进行了替代。
EXPORT(DBSCANKdtreeCluster<pcl::PointXYZ>*)
segment_dbscankdtreecluster_xyz_ctor(){
return new DBSCANKdtreeCluster<pcl::PointXYZ>();
}
二、c#对c++动态库的加载
在c#中调用c++导出的动态库,一般是采用c#中的P/Invoke 技术来实现。P/Invoke 是指通过 Platform Invocation Services(平台调用服务)在托管代码中调用非托管函数或动态库的技术。通过Dllimport导入函数:
//导入对应的动态库以及对应的函数
[DllImport("dbscan_pcl_lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr segment_dbscankdtreecluster_xyz_ctor();
可以通过DependenciesGui去查看动态库的调用约定方式,如下图所示:
可以看到其对应的调用方式都是__cdecl的方式,所以在导入的时候也是指定 CallingConvention = CallingConvention.Cdecl,另外再指定函数名字的时候有两种方式,一种是通过EntryPoint ="函数名"的方式,另一种是将底下的函数命名为同名函数,因为这样就会默认导出的函数名是定义的函数名字。并且参数也要是一一对应的。
所以为了方便就可以对导入的函数进行封装调用,比如把它放进一个类的静态函数中,然后在同一命名空间里的另外一个类就可以封装对其的调用,如下所示:
//做一个命名空间,防止调用冲突
namespace PclSharp
{
//导入c++动态库的函数,使用静态函数的方式进行调用
public static class DLLInvoke
{
[DllImport("dbscan_pcl_lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr segment_dbscankdtreecluster_xyz_ctor();
}
//通过另一个类去封装对前面导入的函数的调用
class DBSCANCluster
{
public IntPtr _ptr;
public DBSCANCluster()
{
_ptr = DLLInvoke.segment_dbscankdtreecluster_xyz_ctor();
}
}
}
三、在要调用的方法实现调用即可
- 主要注意参数那些要对应上。
在main函数中使用如下:
DBSCANCluster ec = new DBSCANCluster();
ec.setMinPts(20);
//构建kd-tree
var tree = new PclSharp.Search.KdTreeOfXYZ();
tree.SetInputCloud(cloud);
ec.setTolerance(0.1);
ec.setMinClusterSize(10);
ec.setMaxClusterSize(200);
ec.setSearchMethod(tree);
ec.setInputCloud(cloud);
var clusterIndices = new VectorOfPointIndices();
ec.extract(clusterIndices);