這篇算是紀錄一下,Heresy 這邊在想把開發環境從 Visual Studio 2019 升級到 Visual Studio 2022 時,踩到的新版編譯器在 lambda expression 這邊的地雷,算是稍微紀錄一下了。
Heresy 這邊踩到的地雷,是 lambda expression + OpenMP 的相容性問題;下面就是一個會出問題的簡單程式碼:
#include <iostream> int main() { auto f = []() { #pragma omp parallel for //Error for (int i = 0; i < 10; ++i) { } }; return 0; }
這個程式碼,基本上就是在 lambda expression 裡面,去使用 OpenMP 來讓迴圈平行化而已。
要編譯的話,可以使用下面的指令:
cl /std:c++20 /openmp test.cpp
在 Visual Studio 2019 的時代,這樣的程式寫法是沒問題的;同時,在 g++ 10 也是沒有問題的。
但是在 Visual Studio 2022 的話,用同樣的指令編譯,則會出現大量的錯誤…
test.cpp(7): error C2760: 語法錯誤: 此處未預期 '#pragma omp parallel'; 預期為 '}' test.cpp(7): error C2059: 語法錯誤: '#pragma omp parallel' test.cpp(8): error C2059: 語法錯誤: ';' test.cpp(9): error C2143: 語法錯誤: 遺漏 ';' (在 '{' 之前) test.cpp(10): error C2143: 語法錯誤: 遺漏 ';' (在 '}' 之前) test.cpp(11): error C2143: 語法錯誤: 遺漏 ';' (在 '}' 之前) test.cpp(13): error C2143: 語法錯誤: 遺漏 ';' (在 '}' 之前) test.cpp(14): error C2143: 語法錯誤: 遺漏 ';' (在 '}' 之前) test.cpp(14): fatal error C1004: 找到未預期的檔案結尾
如果使用 C++17 的標準、或是關閉 OpenMP 的功能的話,則都可以正確地編譯;下面兩個編譯方式,都可以正常地編譯。
cl /std:c++20 /openmp- test.cpp cl /std:c++17 /openmp test.cpp
而如果 OpenMP 的程式不要放在 lambda 裡面,也不會有錯誤。
這個問題其實從預覽版開始就一直都在,Heresy 也有提出回報(《OpenMP in lambda expression compile error》)、也有人有提出類似的問題,但是很遺憾地是就算到了正式版推出了,官方還是沒有很積極的處理…
研究了一下後,發現微軟在 Visual Studio 2019 16.3 的時候,除了本來舊有的 lambda 處理器外,還有引進了所謂的「updated lambda processor」(conforming lambda processor),號稱是比本來的 lambda 處理器更符合 C++ 標準(參考)。
要使用哪個版本的 lambda 處理器,可以透過 /Zc:lambda 來要求使用新版的 lambda 處理器、或是透過 /Zc:lambda- 來使用舊版的 lambda 處理器。
他預設是使用舊版本的處理器,不過在使用 /std:c++20、/std:c++latest 和 /experimental:module 這些編譯參數時,會自動啟用新版本的 lambda 處理器。
而考慮到這樣的狀況,感覺上在 Visual Studio 2022 的環境下,應該就是因為使用 /std:c++20、所以導致它切換到新的 lambda 處理器,結果就踩到地雷了?
所以這邊一個可能的臨時解決方案,就是透過 /Zc:lambda-、強制使用舊版的 lambda 處理器:
cl /std:c++20 /openmp /Zc:lambda- test.cpp
理論上,這樣就暫時可以繞過這個問題了。
不過老實說,這個解法並不算好,只能算是臨時性的方案。
因為當切換回舊版的 lambda 處理器後,對於 lambda expression 的標準支援就會被「降級」,變成沒辦法支援 C++20 的 template lambda。
目前看來舊版的 lambda 處理器應該是有支援到 C++17 的標準,所以 C++14 的 generic lambda 還是可以用的,算是勉強可以代替不支援的 template lambda。
雖然在某些情境下,用 template lambda 還是會比較方便,但是至少還是可以很快地用舊的寫法來替代;相較於此,lambda + OpenMP 的 bug 要找替代方案繞過去,則是相對地更麻煩的。