问题
I'm migrating some code from VS2010 (using boost 1.55) to VS 2015 (using boost 1.60).
I end up with "Microsoft Visual C++ Runtime Library" reporting that abort() has been called
while boost rties to throw an exception. However, I could get it throw other exceptions without any problem (and it used to work 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;
}
This outputs:
Worked
Caught
-> then aborts!
With the debugger, I see that abort is called when error function below (in filesystem/src/operations.cpp) 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 filesystem_error
constructor and can step out of it without any problem, next step (pressed F11 in the debugger, throw
should now be called), abort()
gets called.
Strange thing is that when copy_directory
throws an exception, it also works, and this does call exactly the same error
function in filesystem/src/operations.cpp
.
Call stack upon abort is:
> 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 can't see the source code of ntdll.dll!KiUserExceptionDispatcher()
nor KernelBase.dll!RaiseException()
.
回答1:
boost::filesystem::copy
is a huge broken mess. The function simply calls boost::filesystem::detail::copy
with the third argument defaulted 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 full of invalid dereferences of that potentially-null pointer, and also calls the error code variants of the specific functions that are declared noexcept, passing a bogus reference that resulted from dereferencing the null pointer, which the compiler might well forward as such (remember, we're already in UB land here). These functions in turn take the address of the reference (which typically again yields a null pointer) and call their own detail versions again, which use the error function, which throws if the error code pointer is null.
The workaround:
- Don't use
copy()
, use the concrete function for the type of thing you want if you know it (e.g.copy_file()
), or - Use the version of
copy()
that takes anerror_code
and examine the code yourself.
I see you've already posted a bug report. This bug report is correct.
Edit by jpo38:
Don't use
copy()
Note that this is still the case in boost 1.65.1 recent release. You can prevent developpers from using the function 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 include it for all cpp file using /FI
option. Then you'll get a warning if any code tries to use this messy function.
回答2:
See the boost source code. According to that, BOOST_FILESYSTEM_THROW(EX)
is simply throw EX
. So there must be a reason, why throw calls abort()
. That might be the case, when the exception is thrown while another exception is thrown - e.g. in the exceptions constructor.
For the moment my assumption is a bug in boost::filesystem
. You might consider to file a bug report.
来源:https://stackoverflow.com/questions/34793451/why-is-boostfilesystem-aborting-instead-of-throwing-an-exception