混合 C 和 C++ 导致异常终止
Mixing C and C++ causing exceptions to terminate
我正在将一个项目从 MSVC 移植到 mingw。该项目包含 C 和 C++。但是,无论何时抛出异常,都不会被捕获,而是调用 std::terminate 并且应用程序崩溃。我不明白为什么会收到任何建议。
我的工具链是 cmake/ninja/mingw32,安装在 Windows 的 MSYS2 环境中。
MCVE:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
project(FailedExceptions)
add_executable(FailedExceptions c_funcs.c main.cpp)
//main.cpp
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
int main() {
try {
boost::property_tree::ptree pt;
std::printf("reading file\n");
boost::property_tree::read_xml("nonexistant-file", pt);
std::printf("provider file read\n");
} catch (...) {
std::printf("Exception caught\n");
}
return 0;
}
// c_funcs.c
int SomeCFunction()
{
return 0;
}
输出
$ cmake .. -GNinja
-- The C compiler identification is GNU 7.2.0
-- The CXX compiler identification is GNU 7.2.0
-- Check for working C compiler: C:/msys64/mingw32/bin/cc.exe -- works
-- Check for working CXX compiler: C:/msys64/mingw32/bin/c++.exe -- works
-- Configuring done
-- Generating done
-- Build files have been written to: C:/msys64/home/sferguson/src/vis/build
$ ninja -v
[1/3] C:\msys64\mingw32\bin\cc.exe -MD -MT c_funcs.c.obj -MF c_funcs.c.obj.d -o c_funcs.c.obj -c ../c_funcs.c
[2/3] C:\msys64\mingw32\bin\c++.exe -MD -MT main.cpp.obj -MF main.cpp.obj.d -o main.cpp.obj -c ../main.cpp
[3/3] C:\msys64\mingw32\bin\c++.exe c_funcs.c.obj main.cpp.obj -o FailedExceptions.exe -Wl,--major-image-version,0,--minor-image-version,0 -lgcc_eh -lgcc_eh -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32
$ ./FailedExceptions.exe
reading file
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
$
跟踪:
我可以从 Mingw 博士那里得到这条线索。看起来崩溃确实发生在异常的构造和实际抛出之间。
ntdll.dll!_NtTerminateProcess@8
ntdll.dll!_RtlExitUserProcess@4
kernel32.dll!_ExitProcessStub@4
msvcrt.dll!___crtExitProcess
msvcrt.dll!__cinit
msvcrt.dll!__exit
msvcrt.dll!_abort
FailedExceptions.exe!uw_init_context_1
FailedExceptions.exe!boost::property_tree::xml_parser::xml_parser_error::xml_parser_error
FailedExceptions.exe!boost::property_tree::xml_parser::read_xml<boost::property_tree::basic_ptree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >
FailedExceptions.exe!main
FailedExceptions.exe!__tmainCRTStartup [D:/develop/scripts/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c @ 334]
kernel32.dll!@BaseThreadInitThunk@12
ntdll.dll!___RtlUserThreadStart@8
ntdll.dll!__RtlUserThreadStart@8
疑难解答:
- 我发现一些 5-10 年前的帖子表明这可能是 mingw 的 dw2 和 sjlj 库之间的冲突,但我只安装了
libgcc_s_dw2-1.dll
二进制文件,它随 mingw-w64-i686 一起提供-msys pacman 存储库中的 gcc-libs 包。
- 我尝试更改我的 CMakeLists.txt 文件以使用 C++ 和
project(FailedExceptions LANGUAGES CXX)
编译所有内容。这会阻止 cmake 构建我的 C 文件。所以它确实适用于 MCVE,但我的整个项目缺少所有 C 内容。
- 我添加了
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -fexceptions)
,但似乎没有效果。我已经用 ninja -v
确认这个标志被添加到 C 文件编译命令中。
- 从构建中删除 C 文件后,一切正常。但是即使我没有在这个 MCVE 中使用 C 文件,我仍然在我的大项目中使用它。
- 我找到了另一个更小的例子 here。我可以重现该问题 IFF 我还在同一个项目中编译了一个 C 文件。
这是一个 feature(bug?) in cmake implicit library detection introduced in cmake 3.1。 CMake 认为在 C 模式下 GCC 需要 link 和 gcc_eh
,这会破坏 C++ 异常处理。
您可以通过将此添加到 CMakeLists.txt:
来禁用隐式库检测
set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "")
(不知道如何从列表中排除 gcc_eh
)
RustyX 的答案是公认的答案,但我还发现了另一些似乎有效的解决方法:
set_source_files_properties(filename.c PROPERTIES LANGUAGE CXX)
set(CMAKE_C_COMPILER /path/to/cpp/compiler)
set(APPEND CMAKE_CXX_SOURCE_FILE_EXTENSIONS c)
。
但 RustyX 是迄今为止最好的答案。
编辑:最后一个不起作用。它仍然使用 C 编译器构建。我还需要从 CMAKE_C_SOURCE_FILE_EXTENSIONS
.
中删除 c
要添加到上面的 rustyx 答案(我不允许发表评论),这是根据此拉取请求在 cmake 3.10 中修复的:https://gitlab.kitware.com/cmake/cmake/merge_requests/1460
我使用 CMake 3.10.2 进行了测试,现在似乎无需指定 cmake 节 rustyx 提到的变通方法即可使用异常。
我正在将一个项目从 MSVC 移植到 mingw。该项目包含 C 和 C++。但是,无论何时抛出异常,都不会被捕获,而是调用 std::terminate 并且应用程序崩溃。我不明白为什么会收到任何建议。
我的工具链是 cmake/ninja/mingw32,安装在 Windows 的 MSYS2 环境中。
MCVE:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
project(FailedExceptions)
add_executable(FailedExceptions c_funcs.c main.cpp)
//main.cpp
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
int main() {
try {
boost::property_tree::ptree pt;
std::printf("reading file\n");
boost::property_tree::read_xml("nonexistant-file", pt);
std::printf("provider file read\n");
} catch (...) {
std::printf("Exception caught\n");
}
return 0;
}
// c_funcs.c
int SomeCFunction()
{
return 0;
}
输出
$ cmake .. -GNinja
-- The C compiler identification is GNU 7.2.0
-- The CXX compiler identification is GNU 7.2.0
-- Check for working C compiler: C:/msys64/mingw32/bin/cc.exe -- works
-- Check for working CXX compiler: C:/msys64/mingw32/bin/c++.exe -- works
-- Configuring done
-- Generating done
-- Build files have been written to: C:/msys64/home/sferguson/src/vis/build
$ ninja -v
[1/3] C:\msys64\mingw32\bin\cc.exe -MD -MT c_funcs.c.obj -MF c_funcs.c.obj.d -o c_funcs.c.obj -c ../c_funcs.c
[2/3] C:\msys64\mingw32\bin\c++.exe -MD -MT main.cpp.obj -MF main.cpp.obj.d -o main.cpp.obj -c ../main.cpp
[3/3] C:\msys64\mingw32\bin\c++.exe c_funcs.c.obj main.cpp.obj -o FailedExceptions.exe -Wl,--major-image-version,0,--minor-image-version,0 -lgcc_eh -lgcc_eh -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32
$ ./FailedExceptions.exe
reading file
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
$
跟踪:
我可以从 Mingw 博士那里得到这条线索。看起来崩溃确实发生在异常的构造和实际抛出之间。
ntdll.dll!_NtTerminateProcess@8
ntdll.dll!_RtlExitUserProcess@4
kernel32.dll!_ExitProcessStub@4
msvcrt.dll!___crtExitProcess
msvcrt.dll!__cinit
msvcrt.dll!__exit
msvcrt.dll!_abort
FailedExceptions.exe!uw_init_context_1
FailedExceptions.exe!boost::property_tree::xml_parser::xml_parser_error::xml_parser_error
FailedExceptions.exe!boost::property_tree::xml_parser::read_xml<boost::property_tree::basic_ptree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >
FailedExceptions.exe!main
FailedExceptions.exe!__tmainCRTStartup [D:/develop/scripts/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c @ 334]
kernel32.dll!@BaseThreadInitThunk@12
ntdll.dll!___RtlUserThreadStart@8
ntdll.dll!__RtlUserThreadStart@8
疑难解答:
- 我发现一些 5-10 年前的帖子表明这可能是 mingw 的 dw2 和 sjlj 库之间的冲突,但我只安装了
libgcc_s_dw2-1.dll
二进制文件,它随 mingw-w64-i686 一起提供-msys pacman 存储库中的 gcc-libs 包。 - 我尝试更改我的 CMakeLists.txt 文件以使用 C++ 和
project(FailedExceptions LANGUAGES CXX)
编译所有内容。这会阻止 cmake 构建我的 C 文件。所以它确实适用于 MCVE,但我的整个项目缺少所有 C 内容。 - 我添加了
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -fexceptions)
,但似乎没有效果。我已经用ninja -v
确认这个标志被添加到 C 文件编译命令中。 - 从构建中删除 C 文件后,一切正常。但是即使我没有在这个 MCVE 中使用 C 文件,我仍然在我的大项目中使用它。
- 我找到了另一个更小的例子 here。我可以重现该问题 IFF 我还在同一个项目中编译了一个 C 文件。
这是一个 feature(bug?) in cmake implicit library detection introduced in cmake 3.1。 CMake 认为在 C 模式下 GCC 需要 link 和 gcc_eh
,这会破坏 C++ 异常处理。
您可以通过将此添加到 CMakeLists.txt:
来禁用隐式库检测set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "")
(不知道如何从列表中排除 gcc_eh
)
RustyX 的答案是公认的答案,但我还发现了另一些似乎有效的解决方法:
set_source_files_properties(filename.c PROPERTIES LANGUAGE CXX)
set(CMAKE_C_COMPILER /path/to/cpp/compiler)
set(APPEND CMAKE_CXX_SOURCE_FILE_EXTENSIONS c)
。
但 RustyX 是迄今为止最好的答案。
编辑:最后一个不起作用。它仍然使用 C 编译器构建。我还需要从 CMAKE_C_SOURCE_FILE_EXTENSIONS
.
c
要添加到上面的 rustyx 答案(我不允许发表评论),这是根据此拉取请求在 cmake 3.10 中修复的:https://gitlab.kitware.com/cmake/cmake/merge_requests/1460
我使用 CMake 3.10.2 进行了测试,现在似乎无需指定 cmake 节 rustyx 提到的变通方法即可使用异常。