前几篇的blog都是为了这个实验做基础,先说
原因是python调用数据库150w条数据22s,然后处理数据,其实就2个简单的for循环,65s
需求:
1. python调用c++函数
2. c++调用mysql,查询数据,逻辑处理(暂时不用,稍微复杂)直接打印就好,然后返回给python
3. python收到处理后的数据,打印
实验结果:
c++调用mysql报错mysql.h error到现在也没解决,只能改成c用
结果就是3s处理完了,简直完爆,牛的可怕
涉及知识:
debian系列下c++调用mysql, linux下面安装mysql.h文件
1. python调用简单c或c++代码样例
例子1:
例子1我没测试。
#include<iostream> using namespace std; void foo2(int a,int b) { cout<<a<<" "<<b<<endl; } //编译C++,一定要加extern "C",注意C为大写,小写会无法识别 extern "C" { void cfoo2(int a,int b) { foo2(a,b); } }
g++ -o test1.so -shared -fPIC test1.cpp
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件; -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
import ctypes ll = ctypes.cdll.LoadLibrary lib = ll("./test1.so") lib.cfoo2(3, 4)
例子2:
已测试
c++代码
extern "C" { int sum(){ return 20; } }
标准代码:
#ifdef __cplusplus extern "C" { int sum(){ return 20; } } #endif
为什么?
这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。 要明白为何使用extern "C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.
当然我的超级c++版本
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <mysql/mysql.h> #include <time.h> #include <iostream> #include <string> #include <set> using namespace std; MYSQL *g_conn; // mysql 连接 MYSQL_RES *g_res; // mysql 记录集 MYSQL_ROW g_row; // 字符串数组,mysql 记录行 #define MAX_BUF_SIZE 1024 // 缓冲区最大字节数 const char *g_host_name = "localhost"; const char *g_user_name = "root"; const char *g_password = "python123"; const char *g_db_name = "db_data_20170524164100"; const unsigned int g_db_port = 3306; void print_mysql_error(const char *msg) // 打印最后一次错误 { if (msg) printf("%s: %s ", msg, mysql_error(g_conn)); else puts(mysql_error(g_conn)); } int executesql(const char * sql) { /*query the database according the sql*/ if (mysql_real_query(g_conn, sql, strlen(sql))) // 如果失败 return -1; // 表示失败 return 0; // 成功执行 } int init_mysql() // 初始化连接 { // init the database connection g_conn = mysql_init(NULL); /* connect the database */ if(!mysql_real_connect(g_conn, g_host_name, g_user_name, g_password, g_db_name, g_db_port, NULL, 0)) // 如果失败 return -1; // 是否连接已经可用 executesql("set names utf8"); // 如果失败 // return -1; return 0; // 返回成功 } extern "C" { int mysqlPP() { if (init_mysql()); print_mysql_error(NULL); char sql[MAX_BUF_SIZE]; int firstTime = time((time_t*)NULL); printf("firstTime is: %d ", firstTime); if (executesql("select * from data_stats_gov_cn_diqu_niandu")) // 句末没有分号 print_mysql_error(NULL); g_res = mysql_store_result(g_conn); // 从服务器传送结果集至本地,mysql_use_result直接使用服务器上的记录集 int iNum_rows = mysql_num_rows(g_res); // 得到记录的行数 int iNum_fields = mysql_num_fields(g_res); // 得到记录的列数 printf("共%d个记录,每个记录%d字段 ", iNum_rows, iNum_fields); set<string> myset; set<string>::iterator it; while ((g_row=mysql_fetch_row(g_res))) // 打印结果集 myset.insert(g_row[4]); // spacename int secodnTime = time((time_t*)NULL); printf("secodnTime is: %d ", secodnTime); for (it=myset.begin(); it!=myset.end(); ++it) std::cout << ' ' << *it << ' '; int remainTime = secodnTime - firstTime; printf("remainTime is: %d ", remainTime); mysql_free_result(g_res); // 释放结果集 mysql_close(g_conn); // 关闭链接 return EXIT_SUCCESS; } }
编译c++的命令
g++ -g -o mysql.so -I /usr/include/mysql main.cpp -L /usr/lib/x86_64-linux-gnu/ -lmysqlclient -lz -lpthread -shared -fPIC
编译的时候要注意用到2个路径,mysql.h和libmysqlclient.so的路径
查找mysql.h路径
[root@liu mysql]# find / -name 'mysql.h' /usr/include/mysql/mysql.h
[root@liu mysql]# find / -name '*mysqlclient*' /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.0.0 /usr/lib/x86_64-linux-gnu/libmysqlclient_r.so.18 /usr/lib/x86_64-linux-gnu/libmysqlclient.a /usr/lib/x86_64-linux-gnu/libmysqlclient_r.a /usr/lib/x86_64-linux-gnu/libmysqlclient.so /usr/lib/x86_64-linux-gnu/libmysqlclient_r.so /usr/lib/x86_64-linux-gnu/libmysqlclient_r.so.18.0.0 /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18
python代码
#!/usr/bin/env python # -*- coding:utf-8 -*- import time from ctypes import * def main(): start_time = time.time() result = cdll.LoadLibrary("./test.so") print(result.sum(1, 2)) if __name__ == '__main__': main()
python3在调用c代码或c++代码时向内传递参数
c++代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <mysql/mysql.h> #include <time.h> #include <iostream> #include <string> #include <set> using namespace std; MYSQL *g_conn; // mysql 连接 MYSQL_RES *g_res; // mysql 记录集 MYSQL_ROW g_row; // 字符串数组,mysql 记录行 #define MAX_BUF_SIZE 1024 // 缓冲区最大字节数 void print_mysql_error(const char *msg) // 打印最后一次错误 { if (msg) printf("%s: %s ", msg, mysql_error(g_conn)); else puts(mysql_error(g_conn)); } int executesql(const char * sql) { /*query the database according the sql*/ if (mysql_real_query(g_conn, sql, strlen(sql))) // 如果失败 return -1; // 表示失败 return 0; // 成功执行 } int init_mysql(const char *g_host_name ,const char *g_user_name, const char *g_password, const char *g_db_name, const unsigned int g_db_port) // 初始化连接 { // init the database connection g_conn = mysql_init(NULL); /* connect the database */ if(!mysql_real_connect(g_conn, g_host_name, g_user_name, g_password, g_db_name, g_db_port, NULL, 0)) // 如果失败 return -1; // 是否连接已经可用 executesql("set names utf8"); // 如果失败 // return -1; return 0; // 返回成功 } extern "C" { int mysqlPP( char *g_host_name , char *g_user_name, char *g_password, char *g_db_name, unsigned int g_db_port) { printf("g_host_name: %s ", g_host_name); printf("g_user_name: %s ", g_user_name); printf("g_password: %s ", g_password); printf("g_db_name: %s ", g_db_name); printf("g_db_port: %d ", g_db_port); if (init_mysql( g_host_name , g_user_name, g_password, g_db_name, g_db_port)); print_mysql_error(NULL); char sql[MAX_BUF_SIZE]; int firstTime = time((time_t*)NULL); printf("firstTime is: %d ", firstTime); if (executesql("select * from data_stats_gov_cn_diqu_niandu")) // 句末没有分号 print_mysql_error(NULL); g_res = mysql_store_result(g_conn); // 从服务器传送结果集至本地,mysql_use_result直接使用服务器上的记录集 int iNum_rows = mysql_num_rows(g_res); // 得到记录的行数 int iNum_fields = mysql_num_fields(g_res); // 得到记录的列数 printf("共%d个记录,每个记录%d字段 ", iNum_rows, iNum_fields); set<string> myset; set<string>::iterator it; while ((g_row=mysql_fetch_row(g_res))) // 打印结果集 myset.insert(g_row[4]); // spacename int secodnTime = time((time_t*)NULL); printf("secodnTime is: %d ", secodnTime); for (it=myset.begin(); it!=myset.end(); ++it) std::cout << ' ' << *it << ' '; int remainTime = secodnTime - firstTime; printf("remainTime is: %d ", remainTime); mysql_free_result(g_res); // 释放结果集 mysql_close(g_conn); // 关闭链接 return 0; } }
python代码:
#!/usr/bin/env python # -*- coding:utf-8 -*- import time from ctypes import * def main(): hostname = bytes("127.0.0.1", encoding='utf-8') username = bytes("root", encoding='utf-8') password = bytes("python123", encoding='utf-8') dbname = bytes("db_data_20170524164100", encoding='utf-8') port = 3306 result = cdll.LoadLibrary("./mysql.so") result.mysqlPP(hostname, username, password, dbname, port) if __name__ == '__main__': main()
python3向c, c++传递参数格式转换
#include <stdio.h> #include <string.h> struct test { int key; char* val; }; //传递数值 int ValTest(int n) { printf("val:%d ", n); return 0; } //传递字符串 int strTest(char* pVal) { printf("val:%s ", pVal); return 0; } //传递结构体 int StructTest(struct test data) { printf("key:%d,val:%s ", data.key, data.val); return 0; } //传递结构体指针 int PointTest(struct test* pData) { printf("key:%d,val:%s ", pData->key, pData->val); return 0; } //传递数组 int szTest(int a[], int nLen) { for(int i = 0; i < nLen; i++) { printf("%d ", a[i]); } printf(" "); return 0; }
# -*- coding: utf8 -*- import ctypes from ctypes import * print('*' * 20) print("传递数字") func = ctypes.cdll.LoadLibrary("./pycall.so") func.ValTest(2) print('*' * 20) print("传递字符串") val = bytes('qqqqqq',encoding='utf-8') func.strTest(val) print('*' * 20) print("传递结构体") class testStruct(Structure): _fields_=[('key',c_int),('val',c_char_p)] s = testStruct() s.key = 1 s.val = bytes('test',encoding='utf-8'); func.StructTest(s) print('*' * 20) print("传递结构体指针") func.PointTest(byref(s)) print('*' * 20) print("传递数组") INPUT = c_int * 10 data = INPUT() for i in range(10): data[i] = i func.szTest(data,len(data)) print ('*' * 20)
makefile
#COMPLILER USED CC = gcc AR = ar cru #SO FLAG SHARE = -shared -fPIC TARGET = pycall #EXE PGM AND LIB all: ${TARGET} #MAKE RULE ${TARGET}: $(CC) $@.c $(SHARE) $^ -o $@.so clean: rm -f ${TARGET}.so
最后一步,python调用c++,c++返回数组
参考链接:https://blog.csdn.net/uniqsa/article/details/78603082
优化后的代码,但存在问题
问题1:
代码有缺陷,就是python需要先创建内存地址,可我怎么需要创建多少个地址呢?
你知道吗?未知呀
问题2:
对于我来说,c++技术很差,假设我想要字符串类型的怎么处理呢?
解决方法:
练啊,笨
问题3:
如果是c++创建的内存,是不是要由c++注销呢,可注销了python就拿不到了,可不注销内存,python怎么注销这段内存呢?
一、三的解决方法:
用session的方式呀,缓存呀,笨,是不是傻
技术太渣的忧伤
cpp代码
#include <iostream> using namespace std; typedef unsigned char BYTE; #define MAX_COUNT 20 #if defined(WIN32) || defined(WINDOWS) #define DLL_EXPORT __declspec(dllexport) #else #define DLL_EXPORT #endif struct ByteArrayNew_20_3 { BYTE e[3][20]; }; class TestLib { public: void LogicFuncNew(ByteArrayNew_20_3 &ret); }; void TestLib::LogicFuncNew(ByteArrayNew_20_3 &ret) { ret.e[0][0] = 231; ret.e[0][1] = 11; ret.e[0][2] = 51; ret.e[1][0] = 101; ret.e[1][1] = 201; ret.e[1][2] = 21; cout << "TestLib::LogicFuncNew" << endl; } extern "C" { TestLib oGameLogic; void DLL_EXPORT display2(ByteArrayNew_20_3 *pret) { cout << "cpp display2 func begin" << endl; oGameLogic.LogicFuncNew(*pret); cout << "cpp display2 func end" << endl; } }
py代码
import ctypes class ByteArray_20(ctypes.Structure): """ 这里对应着c++代码里的两层数组的第二层 struct ByteArrayNew_20_3 { BYTE e[3][20]; }; """ _fields_ = [("e1", ctypes.c_ubyte), ("e2", ctypes.c_ubyte), ("e3", ctypes.c_ubyte), ("e4", ctypes.c_ubyte), ("e5", ctypes.c_ubyte), ("e6", ctypes.c_ubyte), ("e7", ctypes.c_ubyte), ("e8", ctypes.c_ubyte), ("e9", ctypes.c_ubyte), ("e10", ctypes.c_ubyte), ("e11", ctypes.c_ubyte), ("e12", ctypes.c_ubyte), ("e13", ctypes.c_ubyte), ("e14", ctypes.c_ubyte), ("e15", ctypes.c_ubyte), ("e16", ctypes.c_ubyte), ("e17", ctypes.c_ubyte), ("e18", ctypes.c_ubyte), ("e19", ctypes.c_ubyte), ("e20", ctypes.c_ubyte)] class ByteArray_20_3(ctypes.Structure): """ 这里对应着第一层 的 3 struct ByteArrayNew_20_3 { BYTE e[3][20]; }; """ _fields_ = [ ("e1", ByteArray_20), ("e2", ByteArray_20), ("e3", ByteArray_20)] so = ctypes.cdll.LoadLibrary lib = so("/opt/code/my_code/test_zip/test2/mysql.so") # 代码有缺陷,就是python需要先创建内存地址,可我怎么需要创建多少个地址呢? # 你知道吗?未知呀 ret = ByteArray_20_3() lib.display2(ctypes.byref(ret)) print(ret.e1.e1) print(ret.e1.e2) print(ret.e1.e3) print(ret.e2.e1) print(ret.e2.e2) print(ret.e2.e3)