【问题标题】:boost iostreams assertion failureboost iostreams 断言失败
【发布时间】:2013-12-30 23:34:17
【问题描述】:

我需要能够使用单个 fstream 来获得独立于平台的文件使用方式。特别是,我需要能够在 Windows 上支持带有 unicode 字符的文件路径,同时尽可能减少对代码的干扰以支持它。因此,似乎 boost iostreams 可以提供答案。但是,在尝试使用它时,我遇到了两次失败,这引起了我的担忧。请看以下示例代码:

// MinGW (MSYS)
// GCC 4.7.2
// Boost 1.50.0
// g++ -g ifstreamtest.cpp -o test.exe -I /t/tools/boost/boost_1_50_0 -L /t/tools/boost/boost_1_50_0/stage/lib -lboost_system-mgw47-mt-d-1_50 -lboost_filesystem-mgw47-mt-d-1_50 -lboost_locale-mgw47-mt-d-1_50 -lboost_iostreams-mgw47-mt-d-1_50

#include <boost/locale.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/filesystem/path.hpp>

namespace MyNamespace
{

typedef ::boost::iostreams::file_descriptor fd;
typedef ::boost::iostreams::stream< ::boost::iostreams::file_descriptor> fstream;
typedef ::boost::iostreams::stream< ::boost::iostreams::file_descriptor_sink> ofstream;
typedef ::boost::iostreams::stream< ::boost::iostreams::file_descriptor_source> ifstream;
} // namespace MyNamespace

int main(int argc, char **argv)
{
    // Imbue boost filesystem codepoint conversions with local system
    // Do this to ensure proper UTF conversion.
    boost::filesystem::path::imbue(boost::locale::generator().generate(""));

    // Test file path.
    boost::filesystem::path file_path("test.txt");


    // Anonymous scope for temporary object.
    {
        // Open file in ctor, write to output, neglect to clean up until dtor.
        MyNamespace::ofstream output(file_path, std::ios_base::out | std::ios_base::app);
        if ( output.is_open() == false ) std::cout << "Unable to open @" << __LINE__ << std::endl;
        output << "test line 1" << std::endl;
        std::cout << "done @" << __LINE__ << std::endl;
    }
    // Temporary object destroyed while still open.

    // Anonymous scope for temporary object.
    {
        // Open file in ctor, write to output, specifically close file.
        MyNamespace::ofstream output1(file_path, std::ios_base::out | std::ios_base::app);
        if ( output1.is_open() == false ) std::cout << "Unable to open @" << __LINE__ << std::endl;
        output1 << "test line 2" << std::endl;
        output1.close();
        std::cout << "done @" << __LINE__ << std::endl;
    }
    // Temporary object destroyed.

    // Anonymous scope for temporary object.
    {
        // Default-ctor; open later. Write to file, neglect to clean up until dtor.
        MyNamespace::ofstream output2;
        // Next line causes "Assertion failed: initialized_, file t:/tools/boost/boost_1_50_0/boost/iostreams/detail/optional.hpp, line 55"
        output2->open(file_path, std::ios_base::out | std::ios_base::app);
        if ( output2.is_open() == false ) std::cout << "Unable to open @" << __LINE__ << std::endl;
        output2 << "blah test test blah" << std::endl;
    }
    // Temporary object destroyed.

//    MyNamespace::ifstream input;
// Compile success, but linker failure:
// s:\reactor\utf8stream/ifstreamtest.cpp:42: undefined reference to `void boost::iostreams::file_descriptor_source::open<boost::filesystem::path>(boost::filesystem::path const&, std::_Ios_Openmode)'
//    input->open(file_path, std::ios_base::in);

    std::cout << "done." << std::endl;
    return 0;
}

在 Windows 上,我仅限于 GCC 4.7.2 和 Boost 1.50。

cmets 解释了这两个失败,但我将在此处对其进行扩展。对我来说,第一个也是最大的问题是当我尝试将流对象用作“普通”fstream 对象时。如果我在其构造函数中打开 fstream,那么一切都很好而且很花哨(可以在前两个匿名范围中看到)。但是,如果我分配 fstream 对象然后尝试稍后打开它,就会发生“坏事”。

如果我尝试调用 boost::iostreams::stream::open(),我会收到编译器错误,指出它无法将参数 1 (boost::filesystem::path) 转换为 boost::iostreams: :file_descriptor_sink。当它可以用 boost::filesystem::path 构建时,为什么它不工作?在任何一种情况下,尝试使用流的 operator->() 调用 boost::iostreams::file_descriptor_sink::open() 都会导致断言失败(如第三个匿名范围所示)。这是非常邪恶的,因为在我看来它应该抛出异常而不是使断言失败。断言失败将向我表明 boost 代码中存在错误。

我遇到的第二个失败是 typedefed fstream 和 ofstream 似乎可以正常工作(好吧,编译和链接)。但是当我尝试使用 ifstream 时,在尝试调用 ifstream->open(); 时出现链接器故障;我在 Windows(如前所述的 MinGW 配置)以及带有Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn) 的 OS X 10.8.5 上得到了这个。由于它编译得很好,并且两者之间的唯一区别是它是源还是接收器......并且两者都应该能够打开文件......这让我认为这也是boost中的一个错误。

有什么想法或建议吗?

【问题讨论】:

    标签: c++ windows boost boost-iostreams


    【解决方案1】:

    关于编译器错误:

    我可以使用 g++ 和 Clang++ 在 Linux 上重现链接错误,所以这不是特定的 Windows 问题(我也在使用 Boost 1.55.0,所以它不是特定于 1.50)。

    我想这是由头文件中允许但未在源/库中实现的模板定义引起的。

    解决方案(仅针对链接问题):而不是

    input->open(file_path, std::ios_base::in);
    

    使用

    input->open(file_path.string(), std::ios_base::in);
    

    这通过使用基于字符串的构造函数来规避可能被错误定义的模板。

    关于断言错误:

    问题是你需要单独初始化file_descriptor_sink,否则iostream初始化将无法正确处理。使用此代码:

    //We need to initialize the sink separately
    boost::iostreams::file_descriptor_sink output2Sink(file_path, std::ios_base::out | std::ios_base::app);
    MyNamespace::ofstream output2(output2Sink);
    if ( output2.is_open() == false ) std::cout << "Unable to open @" << __LINE__ << std::endl;
    output2 << "blah test test blah" << std::endl;
    

    open() 似乎不是导致断言的reset optional

    同样的方法需要应用到MyNamespace::ifstream

    boost::iostreams::file_descriptor_source inputSource(file_path, std::ios_base::in);
    MyNamespace::ifstream input(inputSource);
    //Test reading back what we wrote earlier
    std::string inputContent;
    input >> inputContent;
    //Prints: blah (only the first word is read!)
    std::cout << "Read from test.txt: " << inputContent << std::endl;
    

    另请注意,不必应用解决方案来避免上面的编译器错误。

    通过这些修改,您的程序似乎可以在我的系统上运行。

    【讨论】:

    • 你能重现失败的断言吗?
    • @Keith4G 是的,我愿意(对不起,我写原始答案时没有时间分析这个)。我修改了我的答案以包含有关如何解决您的问题的信息。如果出现任何其他问题,请随时询问。
    • 这似乎有效。然后我还需要打电话给.open() 而不是-&gt;open() 但我认为你已经破解了它。谢谢!
    猜你喜欢
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 2011-05-29
    相关资源
    最近更新 更多