前幾篇的blog都是爲了這個實驗作基礎,先說html
緣由是python調用數據庫150w條數據22s,而後處理數據,其實就2個簡單的for循環,65spython
需求:mysql
1. python調用c++函數linux
2. c++調用mysql,查詢數據,邏輯處理(暫時不用,稍微複雜)直接打印就好,而後返回給pythonios
3. python收處處理後的數據,打印c++
實驗結果:sql
c++調用mysql報錯mysql.h error到如今也沒解決,只能改爲c用數據庫
結果就是3s處理完了,簡直完爆,牛的可怕數組
涉及知識:緩存
debian系列下c++調用mysql, linux下面安裝mysql.h文件
例子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)
已測試
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\n", 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\n", 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字段\n", 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\n", secodnTime); for (it=myset.begin(); it!=myset.end(); ++it) std::cout << ' ' << *it << '\n'; int remainTime = secodnTime - firstTime; printf("remainTime is: %d\n", 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()
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\n", 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\n", g_host_name); printf("g_user_name: %s\n", g_user_name); printf("g_password: %s\n", g_password); printf("g_db_name: %s\n", g_db_name); printf("g_db_port: %d\n", 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\n", 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字段\n", 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\n", secodnTime); for (it=myset.begin(); it!=myset.end(); ++it) std::cout << ' ' << *it << '\n'; int remainTime = secodnTime - firstTime; printf("remainTime is: %d\n", 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()
#include <stdio.h> #include <string.h> struct test { int key; char* val; }; //傳遞數值 int ValTest(int n) { printf("val:%d\n", n); return 0; } //傳遞字符串 int strTest(char* pVal) { printf("val:%s\n", pVal); return 0; } //傳遞結構體 int StructTest(struct test data) { printf("key:%d,val:%s\n", data.key, data.val); return 0; } //傳遞結構體指針 int PointTest(struct test* pData) { printf("key:%d,val:%s\n", pData->key, pData->val); return 0; } //傳遞數組 int szTest(int a[], int nLen) { for(int i = 0; i < nLen; i++) { printf("%d ", a[i]); } printf("\n"); 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
參考連接:https://blog.csdn.net/uniqsa/article/details/78603082
優化後的代碼,但存在問題
問題1:
代碼有缺陷,就是python須要先建立內存地址,可我怎麼須要建立多少個地址呢?
你知道嗎?未知呀
問題2:
對於我來講,c++技術不好,假設我想要字符串類型的怎麼處理呢?
解決方法:
練啊,笨
問題3:
若是是c++建立的內存,是否是要由c++註銷呢,可註銷了python就拿不到了,可不註銷內存,python怎麼註銷這段內存呢?
1、三的解決方法:
用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)