Why does boost :: filesystem break instead of throwing an exception?

I am porting some code from VS2010 (using boost 1.55) to VS 2015 (using boost 1.60).

I end up with "Microsoft Visual C ++ Runtime Library" telling abort() has been called

me that while boost rties is throwing an exception. However, I could get it to throw other exceptions without any problem (and it worked with 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;
}

      

Output:

Worked
Caught
-> then aborts!

      

With the debugger, I can see that abort is called when the error function below (in the /src/operations.cpp filesystem) calls 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;
  }

      

I checked with the debugger and I reach the constructor filesystem_error

and can exit without any problem, the next step (pressing F11 in the debugger, throw

should now be called) calls abort()

.

The weird thing is that when it copy_directory

throws an exception, it also works and it calls the exact same error

function
in filesystem/src/operations.cpp

.

Call stack after interruption:

>   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++

      

But I don't see the source code ntdll.dll!KiUserExceptionDispatcher()

and KernelBase.dll!RaiseException()

.

0


source to share


2 answers


boost::filesystem::copy

- a huge mess. The function just calls boost::filesystem::detail::copy

, the third argument defaults to 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());
    }
  }

      

This function, in turn, is filled with invalid parsing of this potentially-null pointer, and also invokes error code variants for certain functions that are declared noexcept, passing in a false reference resulting from dereferencing a null pointer, which the compiler can evolve well as such (remember that we are already in UB territory here). These functions, in turn, take the reference address (which usually yields a null pointer again) and call their own verbose versions again, which use the error function, which will throw if the error code pointer is null.

Workaround:

  • Don't use copy()

    , use a specific function for the type of thing you want if you know it (for example copy_file()

    ), or
  • Use a version copy()

    that accepts error_code

    and learns the code itself.

I see that you have already posted a bug report. This bug report is correct.




Edit jpo38:

Do not use copy()

Please note that this is still the case in the first 1.65.1 release. You can prevent developers from using this feature by marking it as deprecated:

Create a file containing:

#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) );
}
}

      

And then enable it for all cpp files with the option /FI

. Then you will get a warning if any code tries to use this messy function.

+3


source


See the boost source code . According to this, BOOST_FILESYSTEM_THROW(EX)

simple throw EX

. So there must be a reason why challenge abort()

. This can be the case when an exception is thrown, when another exception is thrown - for example, in the exception constructor.



At the moment, my guess is a bug in boost::filesystem

. You can view the file with the error message.

0


source







All Articles