通过功能打开流

时间:2011-06-29 17:31:17

标签: c++ c++11 copy-constructor istream deleted-functions

我需要[io](f)stream s。

的不可复制性质的帮助

我需要在fstream周围提供一个hackish包装器,以便在Windows上的文件名中处理带有unicode字符的文件。为此,我设计了一个包装函数:

bool open_ifstream( istream &stream, const string &filename )
{
#ifdef __GLIBCXX__
    FILE* result = _wfopen( convert_to_utf16(filename).c_str(), L"r" );
    if( result == 0 )
        return false;

    __gnu_cxx::stdio_filebuf<char>* buffer = new __gnu_cxx::stdio_filebuf<char>( result, std::ios_base::in, 1 );
    istream stream2(buffer);
    std::swap(stream, stream2);

#elif defined(_MSC_VER)
    stream.open( convert_to_utf16(filename) );
#endif
    return !!stream;
}

当然std::swap行是罪魁祸首。我也尝试从函数返回流,但它导致同样的问题。 std::istream的副本构造函数为delete d。我也试过std::move,但没有帮助。我该如何解决这个问题?

编辑:感谢@ tibur的想法,我终于找到了Keep It Simple (TM)的好方法,但功能正常。它仍然是hackish,因为它依赖于所使用的Windows标准C ++库,但由于只有两个真正的正在使用,它对我来说并不是真正的问题。

#include <fstream>
#include <memory>
#if _WIN32
# if __GLIBCXX__
#  include<ext/stdio_filebuf.h>
unique_ptr<istream> open_ifstream( const string &filename )
{
    FILE* c_file = _wfopen( convert_to_utf16(filename).c_str(), L"r" );
    __gnu_cxx::stdio_filebuf<char>* buffer = new __gnu_cxx::stdio_filebuf<char>( c_file, std::ios_base::in, 1 );

    return std::unique_ptr<istream>( new istream(buffer) );
}
# elif _MSC_VER
unique_ptr<ifstream> open_ifstream( const string &filename )
{
    return unique_ptr<ifstream>(new ifstream( convert_to_utf16(filename)) );
}
# else
# error unknown fstream implementation
# endif
#else
unique_ptr<ifstream> open_ifstream( const string &filename )
{
    return unique_ptr<ifstream>(new ifstream(filename) );
}
#endif

在用户代码中:

auto stream_ptr( open_ifstream(filename) );
auto &stream = *stream_ptr;
if( !stream )
    return emit_error( "Unable to open nectar file: " + filename );

这取决于C ++ 0x <memory>auto关键字。当然,你不能只close生成stream变量,但GNU Libstdc ++ std::istream析构函数确实关闭了文件,因此不需要在任何地方进行额外的内存管理。

4 个答案:

答案 0 :(得分:3)

难道你不能只使用rdbuf成员函数直接设置stream的缓冲区吗?

答案 1 :(得分:3)

怎么样:

ifstream * open_ifstream(const string &filename);

答案 2 :(得分:2)

这是一个中度不引人注目的想法:

#include <iconv.h>
#include <algorithm>

void windowify(std::string & filename)
{
#ifdef WIN32
  assert(filename.length() < 1000);

  wchar_t wbuf[1000];
  char    cbuf[1000];
  char * ip = &cbuf[0];
  char * op = reinterpret_cast<char*>(&wbuf[0]);

  size_t ib = filename.length(), ob = 1000;

  std::fill(cbuf + filename.length(), cbuf + 1000, 0);
  std::copy(filename.begin(), filename.end(), cbuf);

  iconv_t cd = iconv_open("WCHAR_T", "UTF-8");
  iconv(cd, &ip, &ib, &op, &ob);
  iconv_close(cd);

  wchar_t sfnbuf[1000];
  std::fill(cbuf, cbuf + 1000, 0);

  ib = GetShortPathNameW(wbuf, sfnbuf, 1000);
  ob = 1000;
  ip = reinterpret_cast<char*>(&wbuf[0]);
  op = &cbuf[0];

  cd = iconv_open("UTF-8", "WCHAR_T");
  iconv(cd, &ip, &ib, &op, &ob);
  iconv_close(cd);

  filename = std::string(cbuf);
#endif
}

用法:

std::string filename = getFilename();
windowify(filename);
std::ifstream infile(filename.c_str());

答案 3 :(得分:1)

我建议稍微改进一下:使用_wopen(或_wsopen_s)代替_wfopen。您将获得一个文件描述符(int),您可以将其传递给stdio_filebuf代替FILE*。这样你应该避免泄漏任何资源(正如marcin所指出的)