提升iostreams断言失败

时间:2013-12-30 23:34:18

标签: c++ windows boost boost-iostreams

我需要能够使用单个fstream来使用与平台无关的文件使用方式。特别是,我需要能够在Windows上支持带有unicode字符的文件路径,尽可能少地侵入代码以支持它。因此,似乎提升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。

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

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

我遇到的第二个失败是typedef ed fstream和ofstream似乎工作(好,编译和链接)就好了。但是当我尝试使用ifstream时,我在尝试调用ifstream-&gt; open()时遇到链接器失败;我在Windows(前面提到的MinGW配置)以及带有Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)的OS X 10.8.5上得到了这个。因为它编译得很好而且两者之间的唯一区别在于它是源还是接收器......并且两者都应该能够打开文件......它让我觉得这也是一个提升中的错误。

有任何想法或建议吗?

1 个答案:

答案 0 :(得分: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;

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

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