序言
看过 人月神话 这部著作的你,一定知道什么叫 没有银弹。程序员之所以这么昂贵是因为经常需要解决一些毫无头绪,困难重重的事情,没有人能够在一旁给你指点迷津, 一切只能靠你自己。 背后需要付出成倍的时间和努力,更多的时间意味着更高的成本,更多的努力需要更多的金钱来弥补精神上遭受的创伤. 你不信?那快来受虐吧.
名词解释
「人月神话」:软件工程 领域 里程碑著作,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?原因如下:
- 股票分析软件需要跨平台,所以选用 wxWidgets-3.1.2 而不是 windows 自带的MFC界面类库, Visual Studio 2017 好用,但是体积过于庞大,安装很慢,而 Code Blocks 17.12 轻便小巧,适合快速开发. ?
- 为什么不用.NET? 因为.NET程序运行需要用户系统里安装.NET运行时环境,否则程序无法运行,会给用户使用上带来不便,而且.NET虚拟机跑起来很慢,用于股票分析这种耗CPU时间的程序并不合适. 更重要的是因为.NET运行时环境的版本问题可能会导致软件安装包体积过于庞大。?
- 用C++访问MySQL数据库,网上还有第三方包 MySQL++,为啥不用它? 因为MySQL++是第三方团队对 MySQL Connector/C接口的包装, 而MySQL Connector/C++是Oracle团队的官方包装, 官方的意味着更好的可维护性。?
- 为啥不用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 ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
一个中级程序员眼中Bug的样子:
Γ
(
n
)
=
(
n
−
1
)
!
∀
n
∈
N
\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N
Γ(n)=(n−1)!∀n∈N
一个高级程序员眼中Bug的样子:
6×5×4×3×2×1 = ?
就像王国维<<人间词话>>里 三重境界一样,
昨天的你
昨夜西风凋碧树。独上高楼,望尽天涯路。
今天的你
衣带渐宽终不悔,为伊消得人憔悴。
明天的你
众里寻他千百度,蓦然回首, 那人却在灯火阑珊处。
code lighter
2019/3/3