看標題應該就知道是用 clang 了,至於目的,當然就是要在 FreeBSD 裡使用最完全最先進的 C++11 來寫程式,並且拋棄 GCC 及 libstdc++。
如果系統中有任何函式庫混入了 libstdc++,導致最終執行檔同時連結了 libc++ 及 libstdc++ (使用 ldd 檢查就能看見),那麼程式在啟動時就會 crash。
因此要達到這個目標,就必須自行編譯整個系統及所有套件,並確實讓使用到 C++ 的程式及函式庫都只連結到 libc++。
最新的 FreeBSD 9.x 已內建 clang,不過內建的版本並不是在這模式下編譯的,因此魯莽地在 make buildworld 時在 /etc/make.conf 的 CXXFLAGS 放 -std=gnu++11 -stdlib=libc++ 是會吃 error 的。
雖然到 FreeBSD 10.x 才會正式拿掉系統的 GCC,不過就像去年在在 FreeBSD 9.1 裡完全使用 clang 代替系統的 gcc這篇中所說的,只要在 /etc/make.conf 放一行 WITHOUT_GCC=yes 就可以在下次 installworld 後使用 make delete-old 和系統的舊版 GCC 徹底說再見。
除此之外,還必須搭上 WITH_CLANG_IS_CC=yes 將系統的 cc 更換成 clang,當然 c++ 及 cpp 也會一併被 clang++ 及 clang-cpp 替換。
至於 /etc/src.conf 能放什麼,只要 man src.conf 就能看到。
首先要重新編譯整個系統,方法在 /usr/src/Makefile 的註解裡有,應該 FreeBSD 的使用者都很熟悉。
我習慣是用的是下面共 11 步的方式來做更新,第二次 mergemaster 會下 -iU 這兩個參數:
12345678910111213141516171819202122 # For individuals wanting to build from the sources currently on their# system, the simple instructions are:## 1. `cd /usr/src' (or to the directory containing your source tree).# 2. Define `HISTORICAL_MAKE_WORLD' variable (see README).# 3. `make world'## For individuals wanting to upgrade their sources (even if only a# delta of a few days):## 1. `cd /usr/src' (or to the directory containing your source tree).# 2. `make buildworld'# 3. `make buildkernel KERNCONF=YOUR_KERNEL_HERE' (default is GENERIC).# 4. `make installkernel KERNCONF=YOUR_KERNEL_HERE' (default is GENERIC).# [steps 3. & 4. can be combined by using the "kernel" target]# 5. `reboot' (in single user mode: boot -s from the loader prompt).# 6. `mergemaster -p'# 7. `make installworld'# 8. `make delete-old'# 9. `mergemaster' (you may wish to use -i, along with -U or -F).# 10. `reboot'# 11. `make delete-old-libs' (in case no 3rd party program uses them anymore)
至於 src.conf 我放的是這樣:
1 2 3 4 |
WITH_LIBCPLUSPLUS=yes WITHOUT_GCC=yes WITH_CLANG_EXTRAS=yes WITH_CLANG_IS_CC=yes |
然後 /etc/make.conf 裡和編譯有關的變數放這樣:
1 2 3 4 5 6 |
CC=clang CXX=clang++ CPP=clang-cpp CFLAGS+=-march=native -O3 CXXFLAGS+=-march=native -O3 |
幾個月前,如果不用 -march=native,根據 CPU 指令集特性的不同,可能必須使用 core-i7、core-i7-avx、core-avx-i、core-avx2 這類參數,而且 core-avx2 還有地雷不能用,否則編出來的東西會噴 illegal instruction。
而目前最新的 GCC 4.9.x 及 clang 3.4.1,-march 吃的參數已經改用 microarchitecture 的名稱了,這個有裝 lang/gcc49 的話可以 man gcc49 看到:
123456789101112131415161718192021 sandybridgeIntel Sandy Bridge CPU with 64-bit extensions, MMX, SSE, SSE2,SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AES and PCLMULinstruction set support.ivybridgeIntel Ivy Bridge CPU with 64-bit extensions, MMX, SSE, SSE2,SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AES, PCLMUL,FSGSBASE, RDRND and F16C instruction set support.haswellIntel Haswell CPU with 64-bit extensions, MOVBE, MMX, SSE,SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES,PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2 and F16C instructionset support.broadwellIntel Broadwell CPU with 64-bit extensions, MOVBE, MMX, SSE,SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES,PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX andPREFETCHW instruction set support.
如果好奇下 -march=native 會偵測到什麼,我們都知道在 GCC 下 gcc -march=native -Q --help=target 然後看 -march 那欄抓到什麼來得知。
至於 clang 的話,可以下 : | clang -v -E -march=native - 然後觀察 -target-cpu 後面的參數來得知。
總之如果偵測錯誤的話,就不要使用 -march=native,是 haswell 的 CPU 就直接下 -march=haswell,自動的東西本來可信度就有限。
接下來就是照上述 11 個步驟重新編譯 kernel 及 world,當然如果 /usr/src 和你目前系統版本相符,也可以只做 world 的部分。
一旦全部做完之後,再修改 /etc/make.conf 的 CXXFLAGS:
1 |
CXXFLAGS+=-march=native -O3 -std=gnu++11 -stdlib=libc++ |
然後重複上述 11 個步驟,或者執行和 world 相關的部分。
理論上只要 /etc/make.conf 這樣放了,後續編譯 ports 裡和 C++ 有關的程式碼,也都會是開啟 -std=gnu++11 -stdlib=libc++ 在編。
不過事與願違,其實有部份 source code 的 Makefile 是寫死 -lstdc++,這個沒辦法,只能靠 sed 處理或手動修改,譬如 libvpx 就會遇到這樣的問題:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
diff -Nrcpad libvpx-1.3.0.orig/libs.mk libvpx-1.3.0/libs.mk *** libvpx-1.3.0.orig/libs.mk Mon Feb 24 04:19:45 2014 --- libvpx-1.3.0/libs.mk Mon Feb 24 04:20:39 2014 *************** $(foreach bin,$(LIBVPX_TEST_BINS),\ --- 482,488 ---- lib$(CODEC_LIB)$(CODEC_LIB_SUF) libgtest.a ))\ $(if $(BUILD_LIBVPX),$(eval $(call linkerxx_template,$(bin),\ $(LIBVPX_TEST_OBJS) \ ! -L. -lvpx -lgtest $(extralibs) -lm)\ )))\ $(if $(LIPO_LIBS),$(eval $(call lipo_bin_template,$(bin))))\ *** 482,488 **** lib$(CODEC_LIB)$(CODEC_LIB_SUF) libgtest.a ))\ $(if $(BUILD_LIBVPX),$(eval $(call linkerxx_template,$(bin),\ $(LIBVPX_TEST_OBJS) \ ! -L. -lvpx -lgtest $(extralibs) -lm -stdlib=libc++)\ )))\ $(if $(LIPO_LIBS),$(eval $(call lipo_bin_template,$(bin))))\ |
編譯 graphviz 的時候也會遇到相同的災難,可以在 make patch 後下 find . -type f | xargs grep 'stdc++' | awk 'BEGIN { FS = ":" } {print $1}' | xargs sed -i .orig 's/lstdc++/lc++/g' 暴力搞定。
find 的用法看人看狀況,這邊我不用 -exec 參數而使用 xargs,這沒什麼強制規定,選用得順的方法就行。
至於 boost,可以在 /usr/ports/devel/boost-libs/Makefile 裡的適當處補一行這個:
1 |
BJAM_ARGS+= toolset=clang cxxflags="-std=gnu++11 -stdlib=libc++" linkflags="-stdlib=libc++" |
當然你會問怎麼不請 ports maintainer 加成 option 就好?原因是我懶,給有空的人去提建議或 send-pr 吧,只是到時可能會有 -std=c++11 和 -std=gnu++11 之爭。
如果你有安裝 databases/mysql++3 的需求,問題看起來很像,但是其實並不相同。
你會發現 -stdlib=libc++ 明明都下了,但是 linker 卻去 link libstdc++,就好像 -stdlib=libc++ 從來沒被下過一樣。
這個現象是 -Wl,-soname,libmysqlpp.so.3 造成的,只要有它在,-stdlib=libc++ 的效果就像是被取消一樣,你必須自己設法手動解決。
至於為什麼會發生這種事?我也沒詳細去追原因,上班族的自由時間是很少的,有空再查吧。
除此之外,你可能還會遇到一些很夭壽的狀況,而這狀況是 ports maintainer 們搞出來的。
比方說,你在編譯 devel/yasm 的時候,就會在 configure 階段死掉:
1 2 |
configure: error: in `/usr/ports/devel/yasm/work/yasm-1.2.0': configure: error: C compiler cannot create executables |
去開 config.log 來看,就會看到這種詭異的東西:
1 2 3 |
configure:3484: clang -O2 -pipe -march=native -O3 -fno-strict-aliasing -O2 -pipe -march=core-avx-i -O3 -fno-strict-aliasing -march=native -O3 -std=gnu++11 -stdlib=libc++ -I/usr/local/include -L/usr/local/lib conftest.c >&5 clang: warning: argument unused during compilation: '-stdlib=libc++' error: invalid argument '-std=gnu++11' not allowed with 'C/ObjC' |
你一定會覺得納悶,明明叫起來的是 clang,為什麼你寫在 CXXFLAGS 裡的東西也會跑出來?
在你打開 /usr/ports/devel/yasm/Makefile 後,你就會得到答案:
1 |
CPPFLAGS+= ${CXXFLAGS} -I${LOCALBASE}/include |
我不清楚這有什麼神奇的理由,總之把這智障無比的 ${CXXFLAGS} 拔了,這玩意就可以正常編譯和安裝。
會幹這種事的只有 yasm 的 ports maintainer 嗎?當然不是,我們來看看 textproc/sphinxsearch/Makefile 裡這精美的註解和內容:
1 2 3 4 5 |
# Yes, the conflation of CPPFLAGS and CXXFLAGS is deliberate. No, # don't ask. GNU_CONFIGURE= yes CPPFLAGS+= ${CXXFLAGS} |
他說 don't ask!看到了沒?他說 don't ask!
WTF 是問句不能用,看來只能用非問句直接罵 fuck you。
當然打開了 C++11 模式,也會引進一些更嚴格的型別檢查機制,譬如編譯 textproc/aspell 的時候就會遇上這兩個錯誤:
1 2 3 4 5 6 7 8 9 |
modules/filter/tex.cpp:177:69: error: comparison between pointer and integer ('const char *' and 'int') if (top.in_what == Parm || top.in_what == Opt || top.do_check == '\0') ~~~~~~~~~~~~ ^ ~~~~ 1 error generated. prog/check_funs.cpp:650:14: error: comparison between pointer and integer ('const char *' and 'int') if (word == '\0') ~~~~ ^ ~~~~ 1 error generated. |
這問題沒什麼了不起,看一下上下文就能知道該怎麼改了。
如果你懶得看上下文,最保險的方法也有先檢查 pointer 是否有效,再照該行原語意幫他補 dereference operator。
同樣的問題在 database/mysql56-server 也會發生,需要進行修正:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
diff -Nrcpad mysql-5.6.21.orig/sql/sql_acl.cc mysql-5.6.21/sql/sql_acl.cc *** mysql-5.6.21.orig/sql/sql_acl.cc Thu Sep 11 21:53:31 2014 --- mysql-5.6.21/sql/sql_acl.cc Mon Oct 20 17:44:35 2014 *************** static int replace_user_table(THD *thd, *** 3212,3218 **** * An empty password is considered to be of mysql_native type. */ ! if (combo->plugin.str == NULL || combo->plugin.str == '\0') { if (combo->uses_identified_by_password_clause) { --- 3212,3218 ---- * An empty password is considered to be of mysql_native type. */ ! if (combo->plugin.str == NULL || *combo->plugin.str == '\0') { if (combo->uses_identified_by_password_clause) { |
編譯 MySQL 5.6 還會遇到另一個問題,不過這裡 clang 已經很好心地提供了解決方法,照著把 int 換成 size_t 就能解決:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
/usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:195:5: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, definitions_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:195:5: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, definitions_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:200:5: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, definition_modes_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:200:5: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, definition_modes_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:205:5: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, definers_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:205:5: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, definers_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:210:5: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, client_cs_names)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:210:5: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, client_cs_names)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:215:5: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, connection_cl_names)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:215:5: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, connection_cl_names)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:220:5: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, db_cl_names)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:220:5: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, db_cl_names)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:229:3: error: non-constant-expression cannot be narrowed from type 'int' to 'size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] static_cast<int>(my_offsetof(class Table_triggers_list, definition_modes_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/ports/databases/mysql56-server/work/mysql-5.6.21/sql/sql_trigger.cc:229:3: note: override this message by inserting an explicit cast static_cast<int>(my_offsetof(class Table_triggers_list, definition_modes_list)), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static_cast<size_t>( ) |
這種 code 在 C++11 模式編譯下堪稱第二大死因,只能說寫 code 的人習慣不好,請他們回去重新唸好書再來寫 code 吧。
因為 mariadb 和 percona 也是 mysql 的衍生物,所以編譯它們同樣可能踩到相同的問題,不過我記得 mariadb 只會遇到上述其中一種問題。
這篇不是 C++11 教學文,而且錯誤訊息已經明確說出原因,我就不多說了。
除了 sql_trigger.cc 以外,sql_view.cc 也會踩到一模一樣的問題,照著 clang 的錯誤訊息修改好就行。
上面講到第二大死因,那麼第一大死因是什麼東西?有沒有第三大死因?我們請 multimedia/mp4v2 進場。
一開始編譯你的眉頭可能就會皺一下,因為看到了 warning message:
1 2 |
clang++ -DHAVE_CONFIG_H -I./include -I./include -I. -I. -Wall -Wformat -O2 -pipe -march=native -O3 -fno-strict-aliasing -march=native -O3 -std=gnu++11 -stdlib=libc++ -fvisibility=hidden -c util/impl.h -o util/impl.h.gch/exe clang++: warning: treating 'c-header' input as 'c++-header' when in C++ mode, this behavior is deprecated |
不過這玩意只是 deprecated,等後續版本突然變成 error 了再處理吧,當然你不爽的話可以直接改了然後 send-pr。
再來就是所謂的第三大死因煞氣進場了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
util/mp4file.cpp:206:14: error: case value evaluates to 4026531848, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_DUMP: ^ util/mp4file.cpp:202:14: error: case value evaluates to 4026531847, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_OPTIMIZE: ^ util/mp4file.cpp:198:14: error: case value evaluates to 4026531846, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_LIST: ^ util/mp4art.cpp:413:14: error: case value evaluates to 4026531852, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_EXTRACT: ^ util/mp4art.cpp:406:14: error: case value evaluates to 4026531851, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_REPLACE: ^ util/mp4art.cpp:402:14: error: case value evaluates to 4026531850, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_REMOVE: ^ util/mp4chaps.cpp:712:14: error: case value evaluates to 4026531855, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHP_REMOVE: ^ util/mp4chaps.cpp:707:14: error: case value evaluates to 4026531851, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHP_CONVERT: ^ util/mp4chaps.cpp:695:14: error: case value evaluates to 4026531854, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHP_IMPORT: ^ util/mp4chaps.cpp:680:14: error: case value evaluates to 4026531853, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHP_EXPORT: ^ util/mp4chaps.cpp:664:14: error: case value evaluates to 4026531852, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHP_EVERY: ^ util/mp4chaps.cpp:659:14: error: case value evaluates to 4026531850, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHP_LIST: ^ util/mp4chaps.cpp:654:14: error: case value evaluates to 4026531849, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHPT_COMMON: ^ util/mp4chaps.cpp:649:14: error: case value evaluates to 4026531848, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHPT_NERO: ^ util/mp4chaps.cpp:644:14: error: case value evaluates to 4026531847, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHPT_QT: ^ util/mp4chaps.cpp:639:14: error: case value evaluates to 4026531846, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_CHPT_ANY: ^ util/mp4art.cpp:395:14: error: case value evaluates to 4026531849, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_ADD: ^ util/mp4art.cpp:391:14: error: case value evaluates to 4026531848, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_LIST: ^ util/mp4art.cpp:382:14: error: case value evaluates to 4026531847, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_ART_INDEX: ^ util/mp4art.cpp:378:14: error: case value evaluates to 4026531846, which cannot be narrowed to type 'int' [-Wc++11-narrowing] case LC_ART_ANY: |
這個現象會發生在 switch statement 裡,基本上也是不會寫程式的典型案例之一。
要想過這一關,你必須從源頭將傳入 switch statement 的變數型別改寬,譬如換成 long long,否則程式的行為就不會是原作者所想要的那樣。
經過一番努力解決第三大死因後,你應該還會在 rtphint.cpp 遇到前面講過的小狀況要處理掉:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
diff -Nrcpad mp4v2-1.9.1.old/src/rtphint.cpp mp4v2-1.9.1/src/rtphint.cpp *** mp4v2-1.9.1.old/src/rtphint.cpp Tue Jul 14 07:07:12 2009 --- mp4v2-1.9.1/src/rtphint.cpp Wed Jul 2 12:01:55 2014 *************** void MP4RtpHintTrack::GetPayload( *** 345,351 **** pSlash = strchr(pSlash, '/'); if (pSlash != NULL) { pSlash++; ! if (pSlash != '\0') { length = strlen(pRtpMap) - (pSlash - pRtpMap); *ppEncodingParams = (char *)MP4Calloc(length + 1); strncpy(*ppEncodingParams, pSlash, length); --- 345,351 ---- pSlash = strchr(pSlash, '/'); if (pSlash != NULL) { pSlash++; ! if (*pSlash != '\0') { length = strlen(pRtpMap) - (pSlash - pRtpMap); *ppEncodingParams = (char *)MP4Calloc(length + 1); strncpy(*ppEncodingParams, pSlash, length); |
不過當這些 source code 的小問題因為 C++11 mode 而一個接一個浮上檯面後,你應該或多或少會打幾個寒顫,特別是這些東西你平常有在用的話...
除此之外,你還會看到另一個小小問題:
1 2 3 4 5 6 7 |
src/mp4.cpp:679:20: error: cannot initialize return object of type 'mp4v2_ismacrypParams *' (aka 'mp4v2_ismacryp_session_params *') with an rvalue of type 'MP4TrackId' (aka 'unsigned int') return MP4_INVALID_TRACK_ID; ^~~~~~~~~~~~~~~~~~~~ ./include/mp4v2/general.h:45:33: note: expanded from macro 'MP4_INVALID_TRACK_ID' #define MP4_INVALID_TRACK_ID ((MP4TrackId)0) /**< Constant: invalid MP4TrackId. */ ^~~~~~~~~~~~~~~ 1 error generated. |
只要你打開 source code 看一下這函式回傳什麼型別,就會看到造成這問題的原因一切都是腦殘加手賤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mp4v2_ismacrypParams *MP4DefaultISMACrypParams(mp4v2_ismacrypParams *ptr) { try { if (ptr == NULL) { ptr = (mp4v2_ismacrypParams *)MP4Malloc(sizeof(mp4v2_ismacrypParams)); } memset(ptr, 0, sizeof(*ptr)); return ptr; } catch (...) { return MP4_INVALID_TRACK_ID; } } |
再來我們編譯 audio/faac 見識見識第一大死因,來看看 clang 精美的錯誤訊息吧:
1 2 3 4 5 6 7 8 9 10 11 |
./mpeg4ip.h:172:17: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] #define D64 "%"D64F ^ ./mpeg4ip.h:173:17: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] #define U64 "%"U64F ^ ./mpeg4ip.h:174:16: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] #define X64 "%"X64F ^ |
是的,正如錯誤訊息所說,C++11 會要求你補空格,否則他會視為 string literal 的 suffix。
我知道你情緒很激動,你可能非常想要殺人,可能你手邊有不知道多少專案也是這樣黏著寫。
但是請接受這樣的事實,然後加油改吧...
改完以後,devel/doxygen 裡面還有類似的東西要你去面對:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
../src/translator_cn.h:113:58: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] { return "成员的完整列表,这些成员属于"CN_SPC; } ^ ../src/translator_cn.h:124:21: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] result = "由"CN_SPC"Doyxgen"CN_SPC"通过分析"CN_SPC; ^ ../src/translator_cn.h:124:36: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] result = "由"CN_SPC"Doyxgen"CN_SPC"通过分析"CN_SPC; ^ ../src/translator_cn.h:124:56: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] result = "由"CN_SPC"Doyxgen"CN_SPC"通过分析"CN_SPC; ^ ../src/translator_cn.h:125:48: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] if (s) result += ((QCString)s+CN_SPC"的"CN_SPC); ^ ../src/translator_cn.h:140:25: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] { return "定义于"CN_SPC; } ^ ../src/translator_cn.h:202:68: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] { return "此继承关系列表按字典顺序粗略的排序:"CN_SPC; } ^ ../src/translator_cn.h:379:44: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] { QCString result=(QCString)"生成于"CN_SPC+date; ^ ../src/translator_cn.h:380:52: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] if (projName) result+=(QCString)CN_SPC", 为"CN_SPC+projName; ^ ../src/translator_cn.h:381:33: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] result+=(QCString)"使用"CN_SPC; ^ ../src/translator_cn.h:387:29: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] return (QCString)"类"CN_SPC+clName+CN_SPC"继承关系图:"; ^ ../src/translator_cn.h:531:24: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] result+=","CN_SPC; ^ ../src/translator_cn.h:533:37: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] result+=CN_SPC", 以及"CN_SPC; ^ ../src/translator_cn.h:543:25: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] { return "继承自"CN_SPC+trWriteList(numEntries)+CN_SPC"."; } ^ ../src/translator_cn.h:550:19: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] return "被"CN_SPC+trWriteList(numEntries)+CN_SPC"继承."; ^ ../src/translator_cn.h:558:22: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] return "重载"CN_SPC+trWriteList(numEntries)+CN_SPC"."; ^ ../src/translator_cn.h:566:19: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] return "被"CN_SPC+trWriteList(numEntries)+CN_SPC"重载."; ^ ../src/translator_cn.h:662:25: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] return "在文件"CN_SPC"@1"CN_SPC"第"CN_SPC"@0"CN_SPC"行定义."; ^ ../src/translator_cn.h:662:35: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] return "在文件"CN_SPC"@1"CN_SPC"第"CN_SPC"@0"CN_SPC"行定义."; |
除了這個第一大死因外,audio/faac 裡有一個 bundle 在一起的 mp4v2,不過和前面遇到的版本不太一樣,所以問題也不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
atom_standard.cpp:25:28: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char name[5]={0251,'n', 'a', 'm', '\0'}; ^~~~ atom_standard.cpp:25:28: note: override this message by inserting an explicit cast static const char name[5]={0251,'n', 'a', 'm', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:26:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char art[5]={0251,'A', 'R', 'T', '\0'}; ^~~~ atom_standard.cpp:26:27: note: override this message by inserting an explicit cast static const char art[5]={0251,'A', 'R', 'T', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:27:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char wrt[5]={0251,'w', 'r', 't', '\0'}; ^~~~ atom_standard.cpp:27:27: note: override this message by inserting an explicit cast static const char wrt[5]={0251,'w', 'r', 't', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:28:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char alb[5]={0251,'a', 'l', 'b', '\0'}; ^~~~ atom_standard.cpp:28:27: note: override this message by inserting an explicit cast static const char alb[5]={0251,'a', 'l', 'b', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:29:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char day[5]={0251,'d', 'a', 'y', '\0'}; ^~~~ atom_standard.cpp:29:27: note: override this message by inserting an explicit cast static const char day[5]={0251,'d', 'a', 'y', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:30:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char too[5]={0251,'t', 'o', 'o', '\0'}; ^~~~ atom_standard.cpp:30:27: note: override this message by inserting an explicit cast static const char too[5]={0251,'t', 'o', 'o', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:31:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char cmt[5]={0251,'c', 'm', 't', '\0'}; ^~~~ atom_standard.cpp:31:27: note: override this message by inserting an explicit cast static const char cmt[5]={0251,'c', 'm', 't', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:32:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char gen[5]={0251,'g', 'e', 'n', '\0'}; ^~~~ atom_standard.cpp:32:27: note: override this message by inserting an explicit cast static const char gen[5]={0251,'g', 'e', 'n', '\0'}; ^~~~ static_cast<char>( ) atom_standard.cpp:33:27: error: constant expression evaluates to 169 which cannot be narrowed to type 'char' [-Wc++11-narrowing] static const char grp[5]={0251,'g', 'r', 'p', '\0'}; ^~~~ atom_standard.cpp:33:27: note: override this message by inserting an explicit cast static const char grp[5]={0251,'g', 'r', 'p', '\0'}; ^~~~ static_cast<char>( ) |
這也是和前面遇到的問題相同,只要依照 clang 親切的提示做修正就行了。
再來,雖然現在 squid 3.3 已經不是最新版了,所以或許你不會想去編譯它。
但是如果你就是手賤,或者為了某些堅持非用 3.3 版不可,那麼在幾個月前你可能會踩到這個該死的問題:
1 2 3 4 5 6 7 8 9 10 11 12 |
In file included from CpuAffinity.cc:10: In file included from ../src/Debug.h:36: In file included from /usr/include/c++/v1/iostream:38: In file included from /usr/include/c++/v1/ios:216: In file included from /usr/include/c++/v1/__locale:15: In file included from /usr/include/c++/v1/string:439: /usr/include/c++/v1/algorithm:2592:66: error: no type named 'value_type' in 'std::__1::iterator_traits<VectorIteratorBase<const Vector<int> > >' __less<typename iterator_traits<_ForwardIterator>::value_type>()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~ CpuAffinity.cc:48:19: note: in instantiation of function template specialization 'std::__1::max_element<VectorIteratorBase<const Vector<int> > >' requested here *std::max_element(Config.cpuAffinityMap->processes().begin(), ^ |
這問題的肇因也是在於作者對 STL 不熟悉,沒有好好瞭解自訂 iterator 的要件是什麼所犯的低級錯誤。
現在 FreeBSD 的 ports 已經有處理這問題的 patch,但還是略嫌囉唆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
--- include/Array.h.orig 2013-05-20 13:48:55.000000000 +0200 +++ include/Array.h 2013-07-15 23:00:06.000000000 +0200 @@ -42,12 +42,19 @@ #include "compat/assert.h" /* iterator support */ +#include <iterator> template <class C> class VectorIteratorBase { public: + typedef typename C::value_type value_type; + typedef std::forward_iterator_tag iterator_category; + typedef typename C::pointer pointer; + typedef typename C::reference reference; + typedef typename C::difference_type difference_type; + VectorIteratorBase(); VectorIteratorBase(C &); VectorIteratorBase(size_t, C &); @@ -79,8 +86,10 @@ public: typedef E value_type; typedef E* pointer; + typedef E& reference; typedef VectorIteratorBase<Vector<E> > iterator; typedef VectorIteratorBase<Vector<E> const> const_iterator; + typedef ptrdiff_t difference_type; void *operator new (size_t); void operator delete (void *); |
如果有讀過那本藍白封面的 Generic Programming and the STL,應該就會知道這個只要讓 VectorIteratorBase 去 public 繼承 std::iterator<>,然後將適當的 template arguments 填入即可。
還有一個已經被原作者拋棄的孤兒,但是大家還是很希望能使用它,這孤兒的名字叫 suphp。
它帶來的錯誤訊息,有幫很多人擦過屁股經驗的,應該一眼就能看出是原作者對 STL 不熟悉,甚至有所誤解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
In file included from IniFile.cpp:21: In file included from /usr/include/c++/v1/string:439: In file included from /usr/include/c++/v1/algorithm:626: /usr/include/c++/v1/utility:294:15: error: no viable overloaded '=' first = __p.first; ~~~~~ ^ ~~~~~~~~~ /usr/include/c++/v1/map:610:15: note: in instantiation of member function 'std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::operator=' requested here {__nc = __v.__cc; return *this;} ^ /usr/include/c++/v1/__tree:1263:35: note: in instantiation of member function 'std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::operator=' requested here __cache->__value_ = *__first; ^ /usr/include/c++/v1/__tree:1204:9: note: in instantiation of function template specialization 'std::__1::__tree<std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::__map_value_compare<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::less<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >::__assign_multi<std::__1::__tree_const_iterator<std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, void *> *, long> >' requested here __assign_multi(__t.begin(), __t.end()); ^ /usr/include/c++/v1/map:1619:21: note: in instantiation of member function 'std::__1::__tree<std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::__map_value_compare<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::less<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::__value_type<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >::operator=' requested here __tree_ = __m.__tree_; ^ ./IniSection.hpp:41:11: note: in instantiation of member function 'std::__1::multimap<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >::operator=' requested here class IniSection { ^ /usr/include/c++/v1/string:1228:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(const basic_string& __str); ^ /usr/include/c++/v1/string:1231:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(basic_string&& __str) ^ /usr/include/c++/v1/string:1235:45: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const _LIBCPP_INLINE_VISIBILITY basic_string& operator=(const value_type* __s) {return assign(__s);} ^ /usr/include/c++/v1/string:1236:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(value_type __c); ^ /usr/include/c++/v1/string:1239:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(initializer_list<value_type> __il) {return assign(__il.begin(), __il.size());} ^ In file included from IniFile.cpp:21: In file included from /usr/include/c++/v1/string:439: In file included from /usr/include/c++/v1/algorithm:626: /usr/include/c++/v1/utility:295:16: error: no viable overloaded '=' second = __p.second; ~~~~~~ ^ ~~~~~~~~~~ /usr/include/c++/v1/string:1228:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(const basic_string& __str); ^ /usr/include/c++/v1/string:1231:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(basic_string&& __str) ^ /usr/include/c++/v1/string:1235:45: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const _LIBCPP_INLINE_VISIBILITY basic_string& operator=(const value_type* __s) {return assign(__s);} ^ /usr/include/c++/v1/string:1236:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(value_type __c); ^ /usr/include/c++/v1/string:1239:19: note: candidate function not viable: 'this' argument has type 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >', but method is not marked const basic_string& operator=(initializer_list<value_type> __il) {return assign(__il.begin(), __il.size());} ^ 2 errors generated. |
原因就是因為他寫了 std::multimap<const std::string, const std::string> 這種鬼東西,const 自己加得不亦樂乎,又一個不看書就亂寫程式的例子。
最後,讓我們來見識一下 C++11 新引進的特性,有請 graphics/silgraphite 進場。
等等...silgraphite 這東西什麼時候用到?難道不能不編嗎?
答案是編譯 print/texlive-full 的時候會跟它相依,所以你還是要編,除非你和我一樣已經是不用寫論文的社會人士;所以編吧。
當然不可免俗地要來一個華麗的爆炸,不過似乎相當單調:
1 2 3 |
../src/segment/GrTableManager.cpp:1365:31: error: reinterpret_cast from 'nullptr_t' to 'gr3ooo::GrSlotState *' is not allowed CalcPositionsUpTo(m_cpass-1, reinterpret_cast<GrSlotState *>(NULL), false, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
如果去查 C++11 對 NULL 這 macro 怎麼定義,就會知道這東西是 implementation-defined,所以我們來看看 implementation 是怎麼定義的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#ifndef NULL #if !defined(__cplusplus) #define NULL ((void *)0) #else #if __cplusplus >= 201103L #define NULL nullptr #elif defined(__GNUG__) && defined(__GNUC__) && __GNUC__ >= 4 #define NULL __null #else #if defined(__LP64__) #define NULL (0L) #else #define NULL 0 #endif /* __LP64__ */ #endif /* __GNUG__ */ #endif /* !__cplusplus */ #endif |
在 C++11 的模式下,這玩意會被定義成 nullptr。
對 C++11 新特性略有耳聞的人應該就會知道 nullptr 不需要也不可以使用 reinterpret_cast 去轉成其它 pointer。
這種情況下有兩種方式可以選擇,一種是利用它先天的隱式型別轉換,剝掉 reinterpret_cast 即可,或者明確寫成 static_cast。
你一定會很好奇 nullptr 到底是什麼東西?不能用 reinterpret_cast,難道它不是 pointer 嗎?它是不是 keyword?它是一個 object 嗎?
歡迎來到 C++11 的世界,我不在這裡告訴你,現在已經 2014 年了,人總是需要更新一些知識才能跟上時代腳步的。
做為這篇發在我自己個板的廢文收尾的錯誤訊息,還是要來點新鮮的東西。
來看看 graphics/silgraphite 裡華麗的爆炸吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
../src/segment/Segment.cpp:580:2: error: no matching function for call to 'swap' std::swap(*this, *pgrseg); ^~~~~~~~~ /usr/include/c++/v1/type_traits:3166:5: note: candidate template ignored: disabled by 'enable_if' [with _Tp = gr3ooo::Segment] is_move_constructible<_Tp>::value && ^ /usr/include/c++/v1/utility:218:1: note: candidate template ignored: could not match '_Tp [_Np]' against 'gr3ooo::Segment' swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value) ^ /usr/include/c++/v1/utility:456:1: note: candidate template ignored: could not match 'pair<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(pair<_T1, _T2>& __x, pair<_T1, _T2>& __y) ^ /usr/include/c++/v1/tuple:166:6: note: candidate template ignored: could not match '__tuple_leaf<_Ip, type-parameter-0-1, >' against 'gr3ooo::Segment' void swap(__tuple_leaf<_Ip, _Hp, _Ep>& __x, __tuple_leaf<_Ip, _Hp, _Ep>& __y) ^ /usr/include/c++/v1/tuple:704:1: note: candidate template ignored: could not match 'tuple<type-parameter-0-0...>' against 'gr3ooo::Segment' swap(tuple<_Tp...>& __t, tuple<_Tp...>& __u) ^ /usr/include/c++/v1/memory:2381:1: note: candidate template ignored: could not match '__compressed_pair<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(__compressed_pair<_T1, _T2>& __x, __compressed_pair<_T1, _T2>& __y) ^ /usr/include/c++/v1/memory:2871:1: note: candidate template ignored: could not match 'unique_ptr<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) _NOEXCEPT {__x.swap(__y);} ^ /usr/include/c++/v1/memory:4861:1: note: candidate template ignored: could not match 'shared_ptr<type-parameter-0-0>' against 'gr3ooo::Segment' swap(shared_ptr<_Tp>& __x, shared_ptr<_Tp>& __y) _NOEXCEPT ^ /usr/include/c++/v1/memory:5150:1: note: candidate template ignored: could not match 'weak_ptr<type-parameter-0-0>' against 'gr3ooo::Segment' swap(weak_ptr<_Tp>& __x, weak_ptr<_Tp>& __y) _NOEXCEPT ^ /usr/include/c++/v1/string:4005:1: note: candidate template ignored: could not match 'basic_string<type-parameter-0-0, type-parameter-0-1, type-parameter-0-2>' against 'gr3ooo::Segment' swap(basic_string<_CharT, _Traits, _Allocator>& __lhs, ^ /usr/include/c++/v1/__mutex_base:254:1: note: candidate template ignored: could not match 'unique_lock<type-parameter-0-0>' against 'gr3ooo::Segment' swap(unique_lock<_Mutex>& __x, unique_lock<_Mutex>& __y) _NOEXCEPT ^ /usr/include/c++/v1/functional:1814:1: note: candidate template ignored: could not match 'function<type-parameter-0-0 (type-parameter-0-1...)>' against 'gr3ooo::Segment' swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) _NOEXCEPT ^ /usr/include/c++/v1/__bit_reference:87:1: note: candidate template ignored: could not match '__bit_reference<type-parameter-0-0, __has_storage_type<type-parameter-0-0>::value>' against 'gr3ooo::Segment' swap(__bit_reference<_Cp> __x, __bit_reference<_Cp> __y) _NOEXCEPT ^ /usr/include/c++/v1/__bit_reference:97:1: note: candidate template ignored: could not match '__bit_reference<type-parameter-0-0, __has_storage_type<type-parameter-0-0>::value>' against 'gr3ooo::Segment' swap(__bit_reference<_Cp> __x, __bit_reference<_Dp> __y) _NOEXCEPT ^ /usr/include/c++/v1/__bit_reference:107:1: note: candidate template ignored: could not match '__bit_reference<type-parameter-0-0, __has_storage_type<type-parameter-0-0>::value>' against 'gr3ooo::Segment' swap(__bit_reference<_Cp> __x, bool& __y) _NOEXCEPT ^ /usr/include/c++/v1/__bit_reference:117:1: note: candidate template ignored: could not match '__bit_reference<type-parameter-0-0, __has_storage_type<type-parameter-0-0>::value>' against 'gr3ooo::Segment' swap(bool& __x, __bit_reference<_Cp> __y) _NOEXCEPT ^ /usr/include/c++/v1/fstream:453:1: note: candidate template ignored: could not match 'basic_filebuf<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(basic_filebuf<_CharT, _Traits>& __x, basic_filebuf<_CharT, _Traits>& __y) ^ /usr/include/c++/v1/fstream:1089:1: note: candidate template ignored: could not match 'basic_ifstream<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(basic_ifstream<_CharT, _Traits>& __x, basic_ifstream<_CharT, _Traits>& __y) ^ /usr/include/c++/v1/fstream:1234:1: note: candidate template ignored: could not match 'basic_ofstream<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(basic_ofstream<_CharT, _Traits>& __x, basic_ofstream<_CharT, _Traits>& __y) ^ /usr/include/c++/v1/fstream:1379:1: note: candidate template ignored: could not match 'basic_fstream<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(basic_fstream<_CharT, _Traits>& __x, basic_fstream<_CharT, _Traits>& __y) ^ /usr/include/c++/v1/__split_buffer:645:1: note: candidate template ignored: could not match '__split_buffer<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(__split_buffer<_Tp, _Allocator>& __x, __split_buffer<_Tp, _Allocator>& __y) ^ /usr/include/c++/v1/vector:3238:1: note: candidate template ignored: could not match 'vector<type-parameter-0-0, type-parameter-0-1>' against 'gr3ooo::Segment' swap(vector<_Tp, _Allocator>& __x, vector<_Tp, _Allocator>& __y) ^ |
很令人意外嗎?
明明 std::swap() 照經驗原本該是一個修計算機概論時就學過的實作,傳兩個 reference 進去,設個暫存變數來幫忙交換,然後就收工了。
以前 C++ 的 std::swap() 用起來似乎也是這樣,怎麼一進 C++11 就變天了?顯然你需要看書。
如果你的個性是喜歡直接看 code,但是卻沒摸過 C++ Template Metaprogramming,不認識 enable_if<>,那麼建議你還是乖乖看書,別直接追 code;我警告過你。
總而言之,書會告訴你,在 C++11 的 std::swap() 已經從 <algorithm> 搬家到 <utility> 去了,這是第一件要注意的事。
再來,在 C++98 / C++03 裡,傳入的引數型別必須同時滿足 copy-constructible 及 assignable 兩項概念的要求。
而在 C++11 裡,變成了必須滿足 move-constructible 及 move-assignable 這兩項概念的要求。
相信很多人對 C++11 新引進的 move semantic 早就不陌生,也知道 C++11 引進了 move constructor 這項特性。
這東西可說是 C++ 程式效能的救星,也能一吐長年被某些程式語言打趴在地上的怨氣。想知道這是什麼嗎?來學 C++11 吧。
滿足 move-assignable 沒什麼難度,就算你沒有自己定義 move assignment operator,只要有個正常的 copy assignment operator 就能滿足了。
copy assignment operator 這東西要存在的難度極低,因為 C++ compiler 會自動幫你產生,所以你不寫也會有,除非你設法去禁止它被使用或被產生。
打開 engine/include/graphite/Segment.h 就能看到 Segment 這個 class 的定義,完全找不到任何 overloaded operators,所以可以放心。
那麼問題必然出在不滿足 move-constructible,所以來看看究竟發生了什麼事。
滿足 move-contructible 其實也沒有難度,就算你沒有提供 move constructor,只要你有個正常的 copy constructor 就能滿足了。
這程式顯然是 legacy code,不會有 C++11 才有的東西,所以問題就出在這個正常的 copy constructor 是否存在。
來簡單掃一下 Segment 定義式的前半段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Segment { // ... public: // Static methods // Constructors, destructors, etc. Segment(); // Segment(ITextSource * pgts, int ichMin, int ichLim, // LineBrk lbStart, LineBrk lbEnd, // bool fStartLine, bool fEndLine, bool fWsRtl); virtual ~Segment(); // Basic copy constructor: Segment(Segment & seg); // ... }; |
嗯...註解有個 Basic copy constructor,看似是有個 constructor 沒錯,但是它正常嗎?
這程式的作者真的認為所有 Segment 的 instances 丟過來,這個 copy constructor 都有辦法接嗎?
我不知道這作者是不是真的這麼認為,但相信有點 C++ 程式設計經驗的應該都知道有一種東西它接不了。那是什麼?答案是 temporary object。
一個 temporary object 先天上具備 const 性質,所以把 temporary object 丟進這種 constructor 裡會 discard cv-qualifier,這件事在 C++ 是死罪。
換言之這個 basic copy constructor 一點都不 basic,請作者正名為 ridiculous copy constructor,寫這什麼鳥蛋。
所以解決方法就是補 const 就收工了,當然還得去定義這 copy constructor 的檔案裡也補一次,光補在 header file 是不夠的。
其實本來不是很想寫這篇,因為每次發文都好花時間,上班以後沒什麼動力寫。
但是因為好久沒發廢文了,想想也應該來發個一篇。
這幾年來不乏各種 C++11 相關的文章,裡面羅列了各種令人感到新奇的新特性,我想那類文章應該也已經夠多了,沒必要特別寫。
不過既然剛好有機會用不良程式碼來介紹 C++11 的新特性,所以就把最近做的事和遇到的狀況收集了一下貼上來。