0

Я переношу некоторый код из VS2010 (используя boost 1.55) в VS 2015 (используя boost 1.60).Почему boost :: filesystem прерывается, а не бросает исключение?

В итоге я получаю «Библиотека времени выполнения Microsoft Visual C++», сообщающую, что abort() has been called, в то время как boost 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! 

С отладчика, я вижу, что прервать вызывается, когда функция ошибки ниже (в файловой системе/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 строи tor и может выйти из него без каких-либо проблем, следующий шаг (нажатие 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++ 

Но я не могу увидеть исходный код ntdll.dll!KiUserExceptionDispatcher() ни KernelBase.dll!RaiseException().

+0

'abort()' вызывает сигнал, который вызывает аномальное завершение ('SIGABRT'), а не исключение. Исключения 'catch' cath исключений, а не сигналов. Это означает, что обнаружена ошибка, и перед запуском исключения вызывается 'abort'. – mikedu95

+1

@ mikedu95 Вопрос, как я вижу, не потому, что 'abort()' не может быть пойман, а скорее почему (и, возможно, где) 'abort()' вообще вызывается. – hvd

+0

@ mikedu95 Но когда я нажимаю «Retry» на всплывающем меню «Microsft Visual C++ Runtime Library», это приводит меня к строке 'BOOST_FILESYSTEM_THROW'. Почему это прерывается, а не бросает? – jpo38

ответ

3

boost::filesystem::copy - это огромный сломанный беспорядок. Функция просто вызывает boost::filesystem::detail::copy с третьим аргументом по умолчанию в нуль:

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

Эта функция, в свою очередь полна недействительных разыменовывает этого потенциально нулевой указатель, а также называет варианты код ошибки конкретных функций, которые объявлены noexcept, пропуская фиктивную ссылку, возникшую в результате разыменования нулевого указателя, который компилятор может передать как таковой (помните, что мы уже находимся в UB-стране здесь). Эти функции, в свою очередь, принимают адрес ссылки (который обычно снова дает нулевой указатель) и снова вызывают свои собственные подробные версии, которые используют функцию ошибки, которая выдает, если указатель кода ошибки имеет значение NULL.

Обойти:

  • Не используйте copy(), использовать конкретную функцию типа, что вы хотите, если вы его знаете (например, copy_file()), или
  • Используйте версию copy(), которая принимает a error_code и проверьте код самостоятельно.

Я вижу, что вы уже опубликовали отчет об ошибке. Этот отчет об ошибке является правильным.


Редактируйте jpo38:

Не используйте copy()

Обратите внимание, что это по-прежнему происходит в подталкивание 1.65.1 недавний выпуск. Вы можете предотвратить developpers с помощью функции, пометив его как осуждается:

Создать файл, содержащий:

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

А затем включить его для всех файлов CPP, используя /FI вариант. Затем вы получите предупреждение, если какой-либо код попытается использовать эту грязную функцию.

+0

Другое обходное решение: удалить 'BOOST_NOEXCEPT' из файла copy_file и перекомпилировать boost? Я сделал это, и он исправил проблему навсегда! – jpo38

+0

@ jpo38 Это все еще оставляет вас на земле UB. Как пример, компилятор может решить, что 'ec' не может быть пустым в первом' if' функции, поэтому безоговорочно вызывать 'bool' преобразование' error_code' с нулевым этим указателем и, таким образом, действительно сбой , Оптимизатор Visual Studio, похоже, слишком консервативен для этого, но GCC и Clang будут. Известная ошибка ядра Linux была обнаружена, когда GCC начал ее выполнять, что привело к тому, что GCC добавила ключ командной строки для отключения этого поведения. –

+0

«пояс и скобки» Я сделаю оба (перекомпилировать + удалить вызовы на 'copy') ;-) – jpo38

0

См. the boost source code. В соответствии с этим BOOST_FILESYSTEM_THROW(EX) - это просто throw EX. Так что должна быть причина, зачем бросать звонки abort(). Это может иметь место, когда исключение выбрасывается, когда возникает другое исключение - например, в конструкторе исключений.

На данный момент мое предположение является ошибкой в ​​boost::filesystem. Вы можете рассмотреть вопрос о создании отчета об ошибке.

+0

Я был бы очень удивлен, если бы нашел такую ​​банальную ошибку в boost ... но это возможность. – jpo38

+0

Заполненный билет на ускорение: https: //svn.boost.орг/ПРОФ/импульс/билет/11914. Я приму ваш ответ, если они скажут, что это действительно ошибка в их коде ... – jpo38

 Смежные вопросы

  • Нет связанных вопросов^_^