c++ python 交互之 swig

工作中准备用python 作为脚本语言来实现一些工作于是就研究 可以和c++ 交互的脚本语言

本来一开始用的lua 但是 lua本身API接口很少 要么自己需要重复封装代码 要么c++ 导出

太多地方需要重复造轮子。之所以选择python 是因为python的包多 直接 import 即可食用

好了 废话不多说直接进入正题

swig 是什么?

SWIG 是一种软件开发工具,将用 C 编写的程序与各种高级编程语言C++。SWIG 用于不同类型的目标语言,包括常见的脚本语言,如 Javascript、Perl、PHP、Python、Tcl 和 Ruby。支持的语言列表还包括非脚本语言,如 C#、D、Go 语言、Java(包括 Android、Lua、OCaml、Octave、Scilab 和 R)。还支持几个解释和编译的方案实现(吉勒、MzScheme/Racket)。SWIG 最常用于创建高级解释或编译编程环境、用户界面,并作为测试和原型设计 C/C++软件的工具。SWIG 通常用于解析 C/C++接口,并生成上述目标语言调用 C/C++代码所需的"粘合代码"。SWIG 还可以以 XML 的形式导出其解析树。SWIG 是自由软件,SWIG 生成的代码与商业和非商业项目兼容。

swig 项目下载

http://www.swig.org/download.html

下载以后你将得到一个压缩包 swig-4.0.1.tar.gz 我下载的是4.0版本的

swig 文档介绍的很详细了

大部分可以在文档中找到答案

下载后解压压缩包得到如下结构的目录

其中 swig.exe 就是我们需要用到的

首先把这个目录添加的系统环境变量 中 的path中去

然后可以进入 目录Examples\

下可以看到 很多语言的使用例子

这里我们进入python 目录可以看到各种c++ 生成python的例子

进入python 目录下后 进入class 目录可以看到 一个很简单 的 c++ 类导出成python 模块的例子

用 Visual Studio 打开 example.dsp 即可 这里有用的 就两种文件

一种是 你的c++ 源文件 头文件 另外一种就是 xxxxx.i的接口文件 这个是 给swig 使用的

swig简单的使用

下面我就简单的用c++给 python 封装一个文件目录监控的例子 来讲吧

首先打开 你的 Visual Studio 新建一个项目 选择win32项目 创建一个 win32 动态库项目



开始写你的c++ 代码

我的代码如下 FileDirectoryMonitor.h

#ifndef __FILEDIRECTORYMONITOR_H__
#define __FILEDIRECTORYMONITOR_H__
#include <string>
#include <memory>
#include <thread>
#include <windows.h>
class POV :public OVERLAPPED
{
public:
POV()
{
Internal = InternalHigh = 0;
Offset = OffsetHigh = 0;
hEvent = NULL;
m_bufferSize = 1024;
m_pData = new BYTE[m_bufferSize];
memset(m_pData, 0, m_bufferSize);
}
~POV() {
if (m_pData) {
delete[] m_pData;
m_pData = nullptr;
}
}
public:
LPVOID m_pData;
DWORD m_bufferSize;
};
class Callback
{
public:
Callback() {};
virtual ~Callback() {};
virtual void run(const char* filename) {};
};
class FileDirectoryMonitor
{
public:
FileDirectoryMonitor();
virtual ~FileDirectoryMonitor();
bool SetMonitorPath(const std::string& strPath, Callback* callBack);
void start();
private:
std::unique_ptr<std::thread> mMonitorThread;
HANDLE hFile;
HANDLE hIoCmp;
POV* povData;
DWORD mCompletionKey;
Callback* mCallBack;
void MonitorThreadProc();
bool mRuning;
};
#endif //__FILEDIRECTORYMONITOR_H__

cpp 代码文件 FileDirectoryMonitor.cpp

#include "FileDirectoryMonitor.h"
#include <locale>
#include <codecvt>
FileDirectoryMonitor::FileDirectoryMonitor()
:hFile(INVALID_HANDLE_VALUE)
,hIoCmp(INVALID_HANDLE_VALUE)
,mCompletionKey(1)
,mCallBack(nullptr)
,mRuning(true)
,povData(nullptr)
{
} FileDirectoryMonitor::~FileDirectoryMonitor()
{
if (mMonitorThread)
{
if (mMonitorThread->joinable())
{
mRuning = false;
mMonitorThread->join();
mMonitorThread.reset();
mMonitorThread = nullptr;
}
}
if (povData)
{
delete povData;
povData = nullptr;
}
} bool FileDirectoryMonitor::SetMonitorPath(const std::string& strPath, Callback* callBack)
{
bool bResult = false;
hFile = CreateFileA(strPath.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return bResult;
}
if (!callBack)
{
return bResult;
}
if (mCallBack)
{
delete mCallBack;
mCallBack = nullptr;
}
mCallBack = callBack;
hIoCmp = CreateIoCompletionPort(hFile, NULL, mCompletionKey, NULL);
if (!hIoCmp)
{
CloseHandle(hFile);
return bResult;
}
povData = new POV();
DWORD dwCbyte = 0;
BOOL isSucceed = ReadDirectoryChangesW(hFile, povData->m_pData
, povData->m_bufferSize
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE
, &dwCbyte, (LPOVERLAPPED)povData
, NULL);
if (isSucceed)
{
bResult = true;
}
return bResult;
} void FileDirectoryMonitor::start()
{
mMonitorThread = std::make_unique<std::thread>(&FileDirectoryMonitor::MonitorThreadProc, this);
mMonitorThread->join();
} void FileDirectoryMonitor::MonitorThreadProc()
{
POV* OverApp = nullptr;
DWORD lpNumberOfBytesTransferred = NULL;
ULONG_PTR lpCompletionKey = NULL;
DWORD dwCbyte = 0;
while (mRuning)
{
BOOL isOK = GetQueuedCompletionStatus(hIoCmp, &lpNumberOfBytesTransferred, &lpCompletionKey, (LPOVERLAPPED*)&OverApp, INFINITE);
if (isOK)
{
//printf("1111\n");
if (!OverApp) {
break;
}
if (OverApp->m_pData)
{
//printf("2222\n");
DWORD dwNextOffset = 0;
auto pnotify = (FILE_NOTIFY_INFORMATION*)(OverApp->m_pData);
do
{
//printf("3333\n");
dwNextOffset = pnotify->NextEntryOffset;
if (pnotify->FileNameLength && pnotify->Action == FILE_ACTION_ADDED)
{
std::wstring filename;
filename.assign(pnotify->FileName, pnotify->FileNameLength);
//printf("4444\n");
if (mCallBack)
{
//printf("5555\n");
std::string strFileName = "zh-CN";
typedef std::codecvt_byname<wchar_t, char, std::mbstate_t> F;
static std::wstring_convert<F> strCnv(new F(strFileName));
strFileName = strCnv.to_bytes(filename);
//printf("6666 \t %s\n",strFileName.c_str());
mCallBack->run(strFileName.c_str());
//printf("7777\n");
}
}
if (dwNextOffset != 0)
{
pnotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pnotify + dwNextOffset);
}
} while (dwNextOffset != 0);
}
}
memset(OverApp->m_pData, 0, OverApp->m_bufferSize);
if (!ReadDirectoryChangesW(hFile, OverApp->m_pData
, OverApp->m_bufferSize
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE
, &dwCbyte, (LPOVERLAPPED)OverApp
, NULL))
{
break;
}
}
}

在 Visual Studio 新建一个 xxx.i 的文件

这个是这个项目的 Directory.i 文件

%module(directors="1") Directory
%include "std_string.i"
%{
#include "FileDirectoryMonitor.h"
%}
%feature("director") Callback;
%include "FileDirectoryMonitor.h"

下面我们来简单介绍下 %module 模块名 这个模块名是 在python中你 import 模块名 的名字

至于为什么我这个 里面为什么会在 %module后面 带了括号 这个是因为 %module 支持带一些配置信息 我这个带的配置信息就是 导出回调函数模块的 具体可以参考 swig 文档中的 回调函数讲解

%{

include "FileDirectoryMonitor.h" // 这里是你需要引用的 头文件

%}

至于%include "FileDirectoryMonitor.h" //是给 swig 生成python 模块的 引入的 可以参考文档

%feature("director") Callback; // 导出一个回调函数类 可以让python 的类继承 这样你就可在python 中 继承这个 类 来重载实现 你的 回调函数了

细节讲解

想生成这个项目你必须安装了 python 环境 需要在 系统环境变量中 添加 PATHON_INCLUDE python 的include 目录路径 以及PYTHON_LIB python 的lib 路径

上面的做好了 需要在 我们的项目 右键属性 -> C/C++ -> 常规 -> 附加包含目录 中添加 的include 目录路径

以及需要在 我们的项目 右键属性 -> 链接器 -> 附加库目录 中添加 python 的lib库目录路径

还需要在 属性 -> 链接器 -> 输出文件 中修改 输出文件名 格式如下_模块名.pyd

下面我们还需要对我们的 xxxxx.i 的接口文件设置编译生成命令

在你的Visual Studio 项目中 选中 xxxxx.i 文件右键属性 -> 配置属性 -> 常规 -> 项类型 选中 自定义生成工具 然后确定 在自定义生成工具 -> 常规 -> 命令行 添加 swig.exe -c++ -python "%(FullPath)"

输出 中 添加 $(InputName)_wrap.cxx



然后 选中 xxxxx.i 右键编译 编译后没错误 就把 `xxxxxx_wrap.cxx`` 文件添加到项目中

然后去生成就可以看到 在你项目当前目录下 看到 _模块名.pyd 以及 模块名.py 这个时候就可以在 当前目录下 写一个 测试的 python脚本测试你的 代码了

我的测试代码 run.py

import Directory
class pycallback(Directory.Callback):
def __init(self):
print("1111")
def run(self,filename):
#print(1111,type(filename))
print(filename) Monitor = Directory.FileDirectoryMonitor()
back = pycallback()
bresult = Monitor.SetMonitorPath("d:\\pdf\\",back)
print(bresult)
Monitor.start()
print("end")

c++ python 交互之 swig的更多相关文章

  1. 工大助手(C#与python交互)

    工大助手(爬虫--C#与python交互) 基本内容 工大助手(桌面版) 实现登陆.查成绩.计算加权平均分等功能 团队人员 13070046 孙宇辰 13070003 张帆 13070004 崔巍 1 ...

  2. Python教程(1.2)——Python交互模式

    上一节已经说过,安装完Python,在命令行输入"python"之后,如果成功,会得到类似于下面的窗口: 可以看到,结尾有3个>符号(>>>).>&g ...

  3. 区分命令行模式和Python交互模式

    命令行模式 在Windows开始菜单选择"命令提示符",就进入到命令行模式,它的提示符类似C:\> Python交互模式 在命令行模式下敲命令python,就看到类似如下的一 ...

  4. redis与python交互

    import redis #连接 r=redis.StrictRedis(host="localhost",port=6379,password="sunck" ...

  5. 命令行以及Python交互模式下python程序的编写

    一.命令行模式 在Windows开始菜单选择“命令提示符”,就进入到命令行模式,它的提示符类似C:\>: 二.Python交互模式 在命令行模式下敲命令python,就看到类似如下的一堆文本输出 ...

  6. PostgreSQL自学笔记:与python交互

    与python交互教程 原文地址:https://www.yiibai.com/html/postgresql/2013/080998.html 1. Python psycopg2 模块APIs 连 ...

  7. Python交互K线工具 K线核心功能+指标切换

    Python交互K线工具 K线核心功能+指标切换 aiqtt团队量化研究,用vn.py回测和研究策略.基于vnpy开源代码,刚开始接触pyqt,开发界面还是很痛苦,找了很多案例参考,但并不能完全满足我 ...

  8. mysql及python交互

    mysql在之前写过一次,那时是我刚刚进入博客,今天介绍一下mysql的python交互,当然前面会把mysql基本概述一下. 目录: 一.命令脚本(mysql) 1.基本命令 2.数据库操作命令 3 ...

  9. 把sublime text打造成python交互终端(windows和Ubuntu)

    作者:tongqingliu 转载请注明出处:http://www.cnblogs.com/liutongqing/p/7015958.html 把sublime text打造成python交互终端 ...

随机推荐

  1. 洛谷P1546 最短网络 Agri-Net(Prim堆优化)

    #include<bits/stdc++.h> using namespace std; ; const int INF=0x3f3f3f3f; inline void read(int ...

  2. Hbase数据模型 列族

  3. Redis源码解析:01简单动态字符串SDS

    Redis没有直接使用C字符串(以'\0'结尾的字符数组),而是构建了一种名为简单动态字符串( simple  dynamic  string, SDS)的抽象类型,并将SDS用作Redis的默认字符 ...

  4. 21Hash算法以及暴雪Hash

    一:哈希表简介 哈希表是一种查找效率极高的数据结构,理想情况下哈希表插入和查找操作的时间复杂度均为O(1),任何一个数据项可以在一个与哈希表长度无关的时间内计算出一个哈希值(key),然后在常量时间内 ...

  5. @游记@ THUWC2019

    目录 @day -???@ @day -30~-1@ @day 0@ @day 1@ @day 2@ @day 3@ @day -???@ 我这个蒟蒻居然收到了 THUWC 的邀请? 那就去试试运气吧 ...

  6. Java中Map/List/Set .

    很实用,分享一下. 简单版本 复杂版本 参考: http://initbinder.com/articles/cheat-sheet-for-selecting-maplistset-in-java. ...

  7. 正则 ?<= 和 ?= 用法 以及零宽断言等概念

    正则 ?<=  和 ?= 用法   参考网址:http://baike.baidu.com/link?url=2zORJF9GOjU8AkmuHDLz9cyl9yiL68PdW3frayzLwW ...

  8. POJ 3159 Candies、

    题意:n个小孩,m个比较(给你两个孩子代号a,b.然后c意味着a比b最多只能少c个糖果),问1和n之间差距最大的糖果数量. 思路:这是一个差分约束思路 不懂得:传送门, 转化一下就是一个SPFA求最短 ...

  9. poj 3675 Telescope (圆与多边形面积交)

    3675 -- Telescope 再来一题.这题的代码还是继续完全不看模板重写的. 题意不解释了,反正就是一个单纯的圆与多边形的交面积. 这题的精度有点搞笑.我用比较高的精度来统计面积,居然wa了. ...

  10. 推荐几个web前端比较实用的网站

    第一次写博客,说实在的有点紧张和兴奋,哈哈哈哈,本人工作了有两年的时间,平时也有做笔记的习惯,但是都做得乱七八糟的,所以就想通过写博客来记录.好了,废话不多说了,先来几个觉得在工作中使用到的,还不错的 ...