为什么boost :: filesystem中止而不是抛出异常?

jpo*_*o38 2 c++ exception-handling boost-filesystem visual-studio-2015

我正在将一些代码从VS2010(使用Boost 1.55)迁移到VS 2015(使用Boost 1.60)。

我最终得到“ Microsoft Visual C ++运行时库”的报告,该报告abort() has been called同时引发了异常。但是,我可以毫无问题地抛出其他异常(并且它过去曾与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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Worked
Caught
-> then aborts!
Run Code Online (Sandbox Code Playgroud)

使用调试器,我看到当下面的错误函数(在filesystem / src / operations.cpp中)调用时,调用了abort 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;
  }
Run Code Online (Sandbox Code Playgroud)

我检查了调试器,然后到达filesystem_error构造函数,可以毫无问题地退出该程序,下一步(在调试器中按F11,throw现在应调用)abort()被调用。

奇怪的是,当copy_directory抛出一个异常,它也适用,而这确实叫一模一样的error功能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++
Run Code Online (Sandbox Code Playgroud)

但是我看不到ntdll.dll!KiUserExceptionDispatcher()nor 的源代码KernelBase.dll!RaiseException()

Seb*_*edl 5

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());
    }
  }
Run Code Online (Sandbox Code Playgroud)

该函数反过来充满了对该潜在为null的指针的无效取消引用,并且还调用了声明为noexcept的特定函数的错误代码变体,传递了由于取消引用了null指针而导致的虚假引用,编译器可能会转发因此(请记住,我们已经在UB国土上了)。这些函数依次获取引用的地址(通常再次产生一个空指针),并再次调用它们自己的详细信息版本,后者使用错误函数,如果错误代码指针为null,则抛出该错误。

解决方法:

  • 不要使用copy(),只要您知道具体的功能(例如copy_file()),即可使用具体功能,或者
  • 使用版本的copy()接受一个error_code和自己检查代码。

我看到您已经发布了一个错误报告。此错误报告是正确的。


通过jpo38编辑:

不要使用 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) );
}
}
Run Code Online (Sandbox Code Playgroud)

然后使用/FI选项将其包括在所有cpp文件中。然后,如果有任何代码尝试使用此凌乱的函数,则会收到警告。