windows + codeblocks + wxWidgets 連接MySQL數據庫

序言

看過 人月神話 這部著作的你,一定知道什麼叫 沒有銀彈。程序員之所以這麼昂貴是因爲經常需要解決一些毫無頭緒,困難重重的事情,沒有人能夠在一旁給你指點迷津, 一切只能靠你自己。 背後需要付出成倍的時間和努力,更多的時間意味着更高的成本,更多的努力需要更多的金錢來彌補精神上遭受的創傷. 你不信?那快來受虐吧.

名詞解釋

「人月神話」:軟件工程 領域 里程碑著作,IBM大型機之父 佛瑞德·布魯克斯 所發表的一篇關於軟件工程的經典論文
「銀彈」: 銀色子彈,在歐洲民間傳說及19世紀以來哥特小說風潮的影響下,銀色子彈往往被描繪成具有驅魔功效的武器,是 針對狼人等超自然怪物的特效武器。後來也被比喻爲具有極端有效性的解決方法,作爲殺手鐗、最強殺招、王牌等的代稱。
「沒有銀彈」:沒有任何技術或管理上的進展, 能夠在十年內使軟件系統項目生產率、 可靠性或簡潔性獲得數量級上的進步。
「ODBC」:開放數據庫連接(Open Database Connectivity,ODBC)是爲解決異構數據庫間的數據共享而產生的一種技術,簡單點講,ODBC就好比你生活中遇到的代理商,代理商通常用錢就能搞定一切供應商問題,而ODBC技術能訪問世界上紛繁複雜,接口各異的數據庫產品[MySQL / Oracle / IBM DB2等]

背景介紹

本人業餘時間需要用C++寫一款股票分析軟件, 因爲股票數據比較龐大,需要保存到數據庫中提高查詢效率. 我的開發環境如下:

  [操作系統] windows 10 家庭中文版 64位
  [編程環境] Code Blocks 17.12 
  [界面類庫] wxWidgets-3.1.2
  [編譯器] MinGW-w64
  [數據庫鏈接器] MySQL Connector/C++ 8.0
  [數據庫服務器] MySQL 5.7

以上軟件除了 [操作系統] 外,其餘都需要獨立下載並安裝,wxWidgets-3.1.2 還需要額外編譯,採用 MinGW-w64 G++ 編譯非常耗時,我的電腦配置是最新款臺式聯想拯救者高配版, 如果你的電腦配置差些,光編譯 wxWidgets-3.1.2 就需要耗費你一上午的時間,而且你還不能中斷,否則要重新來一遍, 這意味着又一個上午過去了. ?

如果你有過C++數據庫編程經驗,你一定會有疑問,我爲什麼選用上面這些奇怪而生僻的軟件來開發,而不選用Windows平臺開發環境霸主Visual Studio 2017,爲什麼不採用最時髦的技術 .NET, 又或者 爲什麼不用MySQL++而用MySQL Connector/C++ 8.0?原因如下:

  1. 股票分析軟件需要跨平臺,所以選用 wxWidgets-3.1.2 而不是 windows 自帶的MFC界面類庫, Visual Studio 2017 好用,但是體積過於龐大,安裝很慢,而 Code Blocks 17.12 輕便小巧,適合快速開發. ?
  2. 爲什麼不用.NET? 因爲.NET程序運行需要用戶系統裏安裝.NET運行時環境,否則程序無法運行,會給用戶使用上帶來不便,而且.NET虛擬機跑起來很慢,用於股票分析這種耗CPU時間的程序並不合適. 更重要的是因爲.NET運行時環境的版本問題可能會導致軟件安裝包體積過於龐大。?
  3. 用C++訪問MySQL數據庫,網上還有第三方包 MySQL++,爲啥不用它? 因爲MySQL++是第三方團隊對 MySQL Connector/C接口的包裝, 而MySQL Connector/C++是Oracle團隊的官方包裝, 官方的意味着更好的可維護性。?
  4. 爲啥不用ODBC數據庫訪問技術?ODBC本身安裝需要用戶配置 ODBC數據源,過程很麻煩,小白用戶根本不懂. 而且MySQL Connector/C++ 不需要 ODBC做中間代理,訪問MySQL服務器更加直接, 沒有中間商在中間賺差價,程序跑起來嗨到飛起. ?

編譯慘案

goto 語句的任性

按照MySQL官方文檔,我順利的寫完了一段數據庫訪問代碼

void Database::Query(){
    sql::mysql::MySQL_Driver *driver = NULL;
    sql::Connection *conn = NULL;
    int count =0; // 注意這句代碼的位置
    driver = sql::mysql::get_driver_instance();
    sql::Statement* stmt = NULL;
    sql::ResultSet * resultSet = NULL;
    if(driver == NULL){
        std::cout<<"mysql driver is NULL."<<std::endl;
        goto DIE;
    }
    conn = driver->connect("tcp:://localhost:3306","root","root");
    if(conn == NULL){
        std::cout<<"mysql connect failed"<<std::endl;
        goto DIE;
    }
    std::cout<<"mysql connect successed"<<std::endl;
    stmt = conn->createStatement();
    if(stmt == NULL){
        std::cout<<"stmt is null"<<std::endl;
        goto DIE;
    }
    stmt->execute("SET CHARSET UTF8");
    stmt->execute("USE test");
    resultSet = stmt->executeQuery("SELECT * from stock");
    while(resultSet->next()){
        std::cout<<"code "<<resultSet->getString("code")<<", name "<<
        resultSet->getString("name")<<std::endl;
        count++;
    }
    std::cout<<"total records: "<<count<<"  items "<<std::endl;
DIE:
    std::cout<<"mysql query failed."<<std::endl;
}

噩夢就此開始了, 首先你看到的上面這段代碼一開始並不是這樣子,而是長這樣子

void Database::Query(){
    sql::mysql::MySQL_Driver *driver = NULL;
    sql::Connection *conn = NULL;
    //省略和上面相同的代碼段.......
    resultSet = stmt->executeQuery("SELECT * from stock");
    int count =0; // 注意這句代碼的位置
    while(resultSet->next()){
        std::cout<<"code "<<resultSet->getString("code")<<", name "<<
        resultSet->getString("name")<<std::endl;
        count++;
    }
    std::cout<<"total records: "<<count<<"  items "<<std::endl;
DIE:
    std::cout<<"mysql query failed."<<std::endl;
}

就是 int count =0這行代碼引起了一個編譯錯誤:

||=== Build: Debug in rocket (compiler: GNU GCC Compiler) ===|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\build_config.h|59|warning: ignoring #pragma ( warning [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\warning.h|46|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|65|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|69|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|70|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|75|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
D:\work_c++\rocket\common\Database.cpp||In member function 'void Database::Query()':|
D:\work_c++\rocket\common\Database.cpp|51|error: jump to label 'DIE' [-fpermissive]|
D:\work_c++\rocket\common\Database.cpp|39|note:   from here|
D:\work_c++\rocket\common\Database.cpp|44|note:   crosses initialization of 'int count'|
D:\work_c++\rocket\common\Database.cpp|51|error: jump to label 'DIE' [-fpermissive]|
D:\work_c++\rocket\common\Database.cpp|33|note:   from here|
D:\work_c++\rocket\common\Database.cpp|44|note:   crosses initialization of 'int count'|
D:\work_c++\rocket\common\Database.cpp|51|error: jump to label 'DIE' [-fpermissive]|
D:\work_c++\rocket\common\Database.cpp|28|note:   from here|
D:\work_c++\rocket\common\Database.cpp|44|note:   crosses initialization of 'int count'|
||=== Build failed: 3 error(s), 6 warning(s) (0 minute(s), 0 second(s)) ===|

這是什麼問題呢?從 crosses initialization of int count錯誤提示中可以看出 int count=0引起的上面編譯錯誤. 百度了下,找到原因.

g++下的goto語句,在跳轉後還有定義,編譯器會認爲變量沒有初始化,所以編譯通不過, 比如我的 int count=0,挪一下 int count=0到goto前面就OK了. ?

__pragma 指令的腥風血雨

修復了goto 引起的問題,開始繼續編譯,又來了一大堆錯誤,這是什麼鬼?shit! 生無可戀! ?

||=== Build: Debug in rocket (compiler: GNU GCC Compiler) ===|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\warning.h|46|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|65|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|69|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|70|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|75|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\build_config.h|59|error: expected constructor, destructor, or type conversion before '(' token|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|83|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|88|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|150|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|154|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|210|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|215|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|280|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function 'void std::__uninitialized_fill_a(_ForwardIterator, _ForwardIterator, const _Tp&, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|329|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|356|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_copy_move(_InputIterator1, _InputIterator1, _InputIterator2, _InputIterator2, _ForwardIterator, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|397|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_move_copy(_InputIterator1, _InputIterator1, _InputIterator2, _InputIterator2, _ForwardIterator, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|425|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_fill_move(_ForwardIterator, _ForwardIterator, const _Tp&, _InputIterator, _InputIterator, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|447|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function 'void std::__uninitialized_move_fill(_InputIterator, _InputIterator, _ForwardIterator, _ForwardIterator, const _Tp&, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|472|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|492|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|496|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|527|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|532|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function 'void std::__uninitialized_default_a(_ForwardIterator, _ForwardIterator, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|605|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_default_n_a(_ForwardIterator, _Size, _Allocator&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|636|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|659|error: '_Construct_novalue' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|659|note: suggested alternative: 'is_constructible'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|663|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|691|error: '_Construct_novalue' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|691|note: suggested alternative: 'is_constructible'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|696|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function '_ForwardIterator std::__uninitialized_copy_n(_InputIterator, _Size, _ForwardIterator, std::input_iterator_tag)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|751|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|756|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h||In function 'std::pair<_InputIterator, _ForwardIterator> std::__uninitialized_copy_n_pair(_InputIterator, _Size, _ForwardIterator, std::input_iterator_tag)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|779|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_uninitialized.h|784|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_tempbuf.h||In destructor 'std::_Temporary_buffer<_ForwardIterator, _Tp>::~_Temporary_buffer()':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_tempbuf.h|167|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_tempbuf.h|194|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_tempbuf.h|199|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_tempbuf.h|205|error: '_Destroy' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_raw_storage_iter.h||In member function 'std::raw_storage_iterator<_OutputIterator, _Tp>& std::raw_storage_iterator<_OutputIterator, _Tp>::operator=(const _Tp&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_raw_storage_iter.h|85|error: '_Construct' is not a member of 'std'|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_raw_storage_iter.h||In member function 'std::raw_storage_iterator<_OutputIterator, _Tp>& std::raw_storage_iterator<_OutputIterator, _Tp>::operator=(_Tp&&)':|
C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_raw_storage_iter.h|95|error: '_Construct' is not a member of 'std'|
||=== Build failed: 34 error(s), 5 warning(s) (0 minute(s), 1 second(s)) ===|

好像問題不是出在我的代碼身上,通過錯誤提示C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\build_config.h|59|error: expected constructor, destructor, or type conversion before '(' token|發現MySQL Connector/C++代碼出問題了,這讓我覺得不可思議.這可是MySQL官方團隊的代碼啊.


  /*
    Warning 4251 is about non dll-interface classes being used by ones exported
    from our DLL (for example std lib classes or Boost ones). Following
    the crowd, we ignore this issue for now.
  */

  __pragma(warning (disable:4251))

#else
  #define CPPCONN_PUBLIC_FUNC

  /*
    These are triggered by, e.g., std::auto_ptr<> which is used by Boost.
  */

  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

#endif    //#ifndef CPPCONN_PUBLIC_FUNC

#endif    //#ifndef _SQL_BUILD_CONFIG_H_

出於直覺,一定是 __pragma(warning (disable:4251))這一句引起的問題,因爲只見過 #pragma這種編譯器寫法,又是百度上一頓狂轟亂炸,找到了 鏈接 介紹__parama的由來, 摘抄如下:

#pragma 指令用於指定計算機或操作系統特定的編譯器功能。 __pragma 關鍵字是 Microsoft 編譯器特有的,可用於在宏定義中編碼 #pragma 指令。

語法
#pragma token-string
__pragma(token-string)

備註
在保留與 C/C++ 語言的總體兼容性的同時,#pragma 指令使每個編譯器均能夠提供特定於計算機和操作系統的功能。
根據定義,#pragma 指令是計算機或操作系統特定的,並且通常對於每個編譯器而言都有所不同。可用於條件語句以提供新的預處理器功能,或爲編譯器提供實現所定義的信息。
token-string 是一系列字符,這些字符提供了特定的編譯器指令和參數(如果有參數)。數字符號(#)必須位於包含 #pragma 指令的行上的第一個非空白字符;空白字符可以分隔數字符號和詞pragma。在 #pragma 之後,編寫轉換器可分析爲預處理標記的所有文本。 #pragma 的參數受宏展開的約束。
如果編譯器發現它無法識別的 #pragma 指令,則會發出警告並繼續編譯。

那怎麼辦?怎麼去掉下面一大堆編譯錯誤? 既然 __pragma#pragma指令功能相近,何不改成 #pragma試試?說時遲,那時快,一頓騷操作, Ctrl + S 一鍵保存. 一個警告框猶如晴天霹靂,讓我猝不及防.
無法保存文件
怎麼辦?這個簡單,右鍵修改文件安全屬性,勾選修改 , 這樣就可以正常保存修改了.
修改文件安全屬性
神奇的事情發生了, 上面一大堆錯誤瞬間消失了,世界一下子清淨了許多,不過又來了個怪物.

代碼無法鏈接的痛苦

||=== Build: Debug in rocket (compiler: GNU GCC Compiler) ===|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\build_config.h|59|warning: ignoring #pragma ( warning [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\warning.h|46|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|65|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|69|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|70|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
C:\Program Files\MySQL\Connector C++ 8.0\include\jdbc\cppconn\exception.h|75|warning: ignoring #pragma warning  [-Wunknown-pragmas]|
obj\Debug\common\Database.o||In function `Database::Query()':|
D:\work_c++\rocket\common\Database.cpp|23|undefined reference to `__imp__ZN3sql5mysql19get_driver_instanceEv'|
||error: ld returned 1 exit status|
||=== Build failed: 2 error(s), 6 warning(s) (0 minute(s), 3 second(s)) ===|

上面的編譯錯誤就是 我寫了一行正確的代碼driver = sql::mysql::get_driver_instance();但是編譯器卻找不到函數get_driver_instance()的引用. 這不由的讓我想起《大話西遊》中的經典臺詞 曾經有一份真摯的愛情放在我面前,我沒有珍惜,等我失去的時候我才後悔莫及,人世間最痛苦的事莫過於此。 可是我明明已經配置了MySql Connector/C++ 的 LIB 庫搜索路徑啊, 這毫無人性. 無奈之下我只好翻牆找答案,經過一番思考,在 Google 中敲下這麼一條驚天地泣鬼神的搜索語句 building mysql connector c++ with mingw
Stack Overflow上找到一條回答

The mysql connector c++ source code is garbage, unfortunately.
It redefines functions that are defined in the standard library and then includes the standard library, or depends on non-standard functions that are not included in every implementation.
Since the standard library does not check to see if its functions and structs have already been defined, you end up with multiple definition errors all over the place.
Also, the problem is made worse for you in that mysql connector has not been ported for mingw. To compile on windows you are required to use visual studio.
It get’s better. They only support visual studio 8 and 9.
If you follow these instructions exactly, http://dev.mysql.com/doc/connector-cpp/en/connector-cpp-installation-source-windows.html then you might be able to get it to work, but you must not deviate from any version numbers. Use an old version of cmake, an old version of visual studio. They have not tested nor do they care about compatibility with any modern versions of build tools.

上面這段話什麼意思呢?簡單翻譯過來就是

mysql connector/c++ 源代碼寫的一堆狗屎,裏面很多代碼片段和C++標準庫中的函數重名了,爲了規避這個問題,MySQL官方團隊使用了 一條編譯器指令 __pragma(warning (disable:4251))去除了函數重定義的警告. 好雞賊. -_-|| 而且MySQL官方團隊壓根就沒有考慮MinGW這個陣營,windows 環境只支持 Visual Studio 編譯 MySQL Connector/C++. 好歹你是國際大公司,這就有點過分了. ?

天無絕人之路

通過上面的查找,總算找到程序無法正常鏈接的真正原因. 從MySQL官方網站下載的 MySQL Connector/C++ 是使用 Visual Studio編譯的Release版本程序,因爲是微軟自己的編譯器,有一些特殊處理,導出來的符號MinGW-GCC 無法正常識別.這下可愁死人了,難道真要換一個編譯環境重新編譯不成?在Google上又是一頓狂搜和閱讀,我找到了一個鏈接 https://github.com/reminisc3/mysql-mingw64-port, 代碼倉庫簡介如下:

Port MySQL C/C++ connector to Mingw64
Libraries included: MySQL C++ Connector 1.1.5 MySQL C Connector 6.1.5
The goal of this project was/is to provide functional support of the build/use of the MySQL C/C++ connector with the Mingw64 toolset.
Out of the box, the MySQL connectors only support visual studio and namely, Microsoft proprietary functionality.
Pre-built 32/64 bit libraries are included in this repo

有人已經成功移植了MySQL C++ connector 到 Mingw64環境,這太意外了, github上果然牛人輩出!

BINGO!

結語

通過上面的情景再現,想必你一定領教了程序員的痛苦和快樂. 所以請善待你身邊每一位程序員, 他們很不容易,爲了賺取所謂的高薪要花去成倍的時間去了解每一行代碼背後的深意,損傷無數個腦細胞,熬過無數個通宵,才能讓編碼看上去不那麼費力.

一個初級程序員眼中Bug的樣子:

Γ(z)=0tz1etdt&ThinSpace;.\Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

一箇中級程序員眼中Bug的樣子:
Γ(n)=(n1)!nN\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N

一個高級程序員眼中Bug的樣子:
6×5×4×3×2×1 = ?

就像王國維<<人間詞話>>裏 三重境界一樣,
昨天的你
昨夜西風凋碧樹。獨上高樓,望盡天涯路。
今天的你
衣帶漸寬終不悔,爲伊消得人憔悴。
明天的你
衆裏尋他千百度,驀然回首, 那人卻在燈火闌珊處。

code lighter
2019/3/3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章