为什么 boost::filesystem 中止而不是抛出异常?
Why is boost::filesystem aborting instead of throwing an exception?
我正在将一些代码从 VS2010(使用 boost 1.55)迁移到 VS 2015(使用 boost 1.60)。
我最终 "Microsoft Visual C++ Runtime Library" 报告说 abort() has been called
同时提升 rties 抛出异常。但是,我可以让它毫无问题地抛出其他异常(它曾经与 VS2010/boost1.55 一起工作):
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <iostream>
int main( int argc, char* argv[] )
{
// Stepping to folder:
try
{
boost::filesystem::current_path("B:/dev/msvc2015/vobs_bci/public/tst/base/cppunit/utlfile");
std::cout << "Worked" << std::endl; // works OK
}
catch (...)
{
}
// test throwing upon copy_directory because dource folder does not exist:
try
{
boost::filesystem::copy_directory("s", "b");
}
catch (...)
{
std::cout << "Caught" << std::endl; // works OK
}
// test throwing upon copy because target file already exists:
try
{
boost::filesystem::copy("./test.h", "./copied.cpp"); // works
boost::filesystem::copy("./test.h", "./copied.cpp"); // should throw and be caught
}
catch (...)
{
std::cout << "Caught" << std::endl; // never reached...
}
std::cout << "Done" << std::endl;
return 0;
}
这输出:
Worked
Caught
-> then aborts!
使用调试器,我看到当下面的错误函数(在 filesystem/src/operations.cpp 中)调用 BOOST_FILESYSTEM_THROW
:
时调用了 abort
bool error(err_t error_num, const path& p1, const path& p2, error_code* ec,
const char* message)
{
if (!error_num)
{
if (ec != 0) ec->clear();
}
else
{ // error
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error(message,
p1, p2, error_code(error_num, system_category()))); // << Here!
else
ec->assign(error_num, system_category());
}
return error_num != 0;
}
我检查了调试器,我到达了 filesystem_error
构造函数并且可以毫无问题地退出它,下一步(在调试器中按 F11,现在应该调用 throw
), abort()
被调用。
奇怪的是,当copy_directory
抛出异常时,它也有效,并且这确实调用了完全相同的error
函数 in filesystem/src/operations.cpp
.
中止时的调用堆栈是:
> ntdll.dll!KiUserExceptionDispatcher() Inconnu
KernelBase.dll!RaiseException() Inconnu
vcruntime140d.dll!_CxxThrowException(void * pExceptionObject=0x000000000019f670, const _s__ThrowInfo * pThrowInfo=0x000000013fd01870) Ligne 136 C++
test_3rdparty_inprg_boost.exe!`anonymous namespace'::error(unsigned long error_num=80, const boost::filesystem::path & p1={...}, const boost::filesystem::path & p2={...}, boost::system::error_code * ec=0x0000000000000000, const char * message=0x000000013fcf6fb8) Ligne 321 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::detail::copy_file(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}, boost::filesystem::detail::copy_option option=none, boost::system::error_code * ec=0x0000000000000000) Ligne 919 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::copy_file(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}, boost::filesystem::copy_option option=none, boost::system::error_code & ec) Ligne 550 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::detail::copy(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}, boost::system::error_code * ec=0x0000000000000000) Ligne 894 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::copy(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}) Ligne 524 C++
test_3rdparty_inprg_boost.exe!main(int argc=1, char * * argv=0x00000000003f3cc0) Ligne 35 C++
test_3rdparty_inprg_boost.exe!invoke_main() Ligne 75 C++
但是我看不到ntdll.dll!KiUserExceptionDispatcher()
和KernelBase.dll!RaiseException()
的源代码。
参见 the boost source code。据此,BOOST_FILESYSTEM_THROW(EX)
就是 throw EX
。所以肯定是有原因的,为什么会抛出调用abort()
。可能是这种情况,当抛出异常时抛出另一个异常 - 例如在异常构造函数中。
目前我的假设是 boost::filesystem
中的错误。您可能会考虑提交错误报告。
boost::filesystem::copy
是一团糟。该函数简单地调用 boost::filesystem::detail::copy
,第三个参数默认为 null:
BOOST_FILESYSTEM_DECL
void copy(const path& from, const path& to, system::error_code* ec)
{
file_status s(symlink_status(from, *ec));
if (ec != 0 && *ec) return;
if(is_symlink(s))
{
copy_symlink(from, to, *ec);
}
else if(is_directory(s))
{
copy_directory(from, to, *ec);
}
else if(is_regular_file(s))
{
copy_file(from, to, fs::copy_option::fail_if_exists, *ec);
}
else
{
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy",
from, to, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category())));
ec->assign(BOOST_ERROR_NOT_SUPPORTED, system_category());
}
}
这个函数又充满了对该 potentially-null 指针的无效解引用,并且还调用了声明为 noexcept 的特定函数的错误代码变体,传递了一个由解引用空指针产生的虚假引用,编译器可能会这样转发(记住,我们已经在 UB 土地上了)。这些函数依次获取引用的地址(通常再次产生空指针)并再次调用它们自己的详细版本,这些版本使用错误函数,如果错误代码指针为空则抛出。
解决方法:
- 不要使用
copy()
,如果您知道的话,请使用您想要的具体函数类型(例如 copy_file()
),或者
- 使用带有
error_code
的 copy()
版本并自行检查代码。
我看到您已经发布了错误报告。此错误报告是正确的。
jpo38 编辑:
Don't use copy()
请注意,在最近发布的 boost 1.65.1 中仍然如此。您可以通过将其标记为已弃用来阻止开发人员使用该函数:
创建包含以下内容的文件:
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif
...
namespace boost
{
namespace filesystem
{
class path;
DEPRECATED( void copy(const path& from, const path& to) );
}
}
然后使用 /FI
选项将其包含在所有 cpp 文件中。如果有任何代码试图使用这个乱七八糟的函数,您将收到警告。
我正在将一些代码从 VS2010(使用 boost 1.55)迁移到 VS 2015(使用 boost 1.60)。
我最终 "Microsoft Visual C++ Runtime Library" 报告说 abort() has been called
同时提升 rties 抛出异常。但是,我可以让它毫无问题地抛出其他异常(它曾经与 VS2010/boost1.55 一起工作):
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <iostream>
int main( int argc, char* argv[] )
{
// Stepping to folder:
try
{
boost::filesystem::current_path("B:/dev/msvc2015/vobs_bci/public/tst/base/cppunit/utlfile");
std::cout << "Worked" << std::endl; // works OK
}
catch (...)
{
}
// test throwing upon copy_directory because dource folder does not exist:
try
{
boost::filesystem::copy_directory("s", "b");
}
catch (...)
{
std::cout << "Caught" << std::endl; // works OK
}
// test throwing upon copy because target file already exists:
try
{
boost::filesystem::copy("./test.h", "./copied.cpp"); // works
boost::filesystem::copy("./test.h", "./copied.cpp"); // should throw and be caught
}
catch (...)
{
std::cout << "Caught" << std::endl; // never reached...
}
std::cout << "Done" << std::endl;
return 0;
}
这输出:
Worked
Caught
-> then aborts!
使用调试器,我看到当下面的错误函数(在 filesystem/src/operations.cpp 中)调用 BOOST_FILESYSTEM_THROW
:
bool error(err_t error_num, const path& p1, const path& p2, error_code* ec,
const char* message)
{
if (!error_num)
{
if (ec != 0) ec->clear();
}
else
{ // error
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error(message,
p1, p2, error_code(error_num, system_category()))); // << Here!
else
ec->assign(error_num, system_category());
}
return error_num != 0;
}
我检查了调试器,我到达了 filesystem_error
构造函数并且可以毫无问题地退出它,下一步(在调试器中按 F11,现在应该调用 throw
), abort()
被调用。
奇怪的是,当copy_directory
抛出异常时,它也有效,并且这确实调用了完全相同的error
函数 in filesystem/src/operations.cpp
.
中止时的调用堆栈是:
> ntdll.dll!KiUserExceptionDispatcher() Inconnu
KernelBase.dll!RaiseException() Inconnu
vcruntime140d.dll!_CxxThrowException(void * pExceptionObject=0x000000000019f670, const _s__ThrowInfo * pThrowInfo=0x000000013fd01870) Ligne 136 C++
test_3rdparty_inprg_boost.exe!`anonymous namespace'::error(unsigned long error_num=80, const boost::filesystem::path & p1={...}, const boost::filesystem::path & p2={...}, boost::system::error_code * ec=0x0000000000000000, const char * message=0x000000013fcf6fb8) Ligne 321 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::detail::copy_file(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}, boost::filesystem::detail::copy_option option=none, boost::system::error_code * ec=0x0000000000000000) Ligne 919 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::copy_file(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}, boost::filesystem::copy_option option=none, boost::system::error_code & ec) Ligne 550 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::detail::copy(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}, boost::system::error_code * ec=0x0000000000000000) Ligne 894 C++
test_3rdparty_inprg_boost.exe!boost::filesystem::copy(const boost::filesystem::path & from={...}, const boost::filesystem::path & to={...}) Ligne 524 C++
test_3rdparty_inprg_boost.exe!main(int argc=1, char * * argv=0x00000000003f3cc0) Ligne 35 C++
test_3rdparty_inprg_boost.exe!invoke_main() Ligne 75 C++
但是我看不到ntdll.dll!KiUserExceptionDispatcher()
和KernelBase.dll!RaiseException()
的源代码。
参见 the boost source code。据此,BOOST_FILESYSTEM_THROW(EX)
就是 throw EX
。所以肯定是有原因的,为什么会抛出调用abort()
。可能是这种情况,当抛出异常时抛出另一个异常 - 例如在异常构造函数中。
目前我的假设是 boost::filesystem
中的错误。您可能会考虑提交错误报告。
boost::filesystem::copy
是一团糟。该函数简单地调用 boost::filesystem::detail::copy
,第三个参数默认为 null:
BOOST_FILESYSTEM_DECL
void copy(const path& from, const path& to, system::error_code* ec)
{
file_status s(symlink_status(from, *ec));
if (ec != 0 && *ec) return;
if(is_symlink(s))
{
copy_symlink(from, to, *ec);
}
else if(is_directory(s))
{
copy_directory(from, to, *ec);
}
else if(is_regular_file(s))
{
copy_file(from, to, fs::copy_option::fail_if_exists, *ec);
}
else
{
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy",
from, to, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category())));
ec->assign(BOOST_ERROR_NOT_SUPPORTED, system_category());
}
}
这个函数又充满了对该 potentially-null 指针的无效解引用,并且还调用了声明为 noexcept 的特定函数的错误代码变体,传递了一个由解引用空指针产生的虚假引用,编译器可能会这样转发(记住,我们已经在 UB 土地上了)。这些函数依次获取引用的地址(通常再次产生空指针)并再次调用它们自己的详细版本,这些版本使用错误函数,如果错误代码指针为空则抛出。
解决方法:
- 不要使用
copy()
,如果您知道的话,请使用您想要的具体函数类型(例如copy_file()
),或者 - 使用带有
error_code
的copy()
版本并自行检查代码。
我看到您已经发布了错误报告。此错误报告是正确的。
jpo38 编辑:
Don't use
copy()
请注意,在最近发布的 boost 1.65.1 中仍然如此。您可以通过将其标记为已弃用来阻止开发人员使用该函数:
创建包含以下内容的文件:
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif
...
namespace boost
{
namespace filesystem
{
class path;
DEPRECATED( void copy(const path& from, const path& to) );
}
}
然后使用 /FI
选项将其包含在所有 cpp 文件中。如果有任何代码试图使用这个乱七八糟的函数,您将收到警告。