通过函数打开流

发布于 2024-11-17 18:32:44 字数 2247 浏览 8 评论 0原文

我需要有关 [io](f)stream 的不可复制性质的帮助。

我需要为 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 的复制构造函数是 deleted。我还尝试了 std::move 但这没有帮助。我该如何解决这个问题?

编辑:感谢@tibur的想法,我终于找到了一种Keep It Simple (TM)但又实用的好方法。从某种意义上说,它仍然很黑客,因为它依赖于所使用的 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 auto 关键字。当然,您不能只是close生成的stream变量,但是GNU Libstdc++ std::istream析构函数确实负责关闭文件,因此任何地方都不需要额外的内存管理。

I need help with the non-copyable nature of [io](f)streams.

I need to provide a hackish wrapper around fstreams in order to handle files with unicode characters in their filenames on Windows. For this, I devised a wrapper function:

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

With of course the std::swap line being the culprit. I also tried returning the stream from the function, but it leads to the same problem. The copy constructor of a std::istream is deleted. I also tried a std::move but that didn't help. How do I work around this problem?

EDIT: I finally found a good way to Keep It Simple (TM) and yet functional, thanks to @tibur's idea. It's still hackish in the sense that it depends on the Windows Standard C++ library used, but as there's only two real ones in use, it's not really a problem for me.

#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

And in user code:

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

Which depends on C++0x <memory> and the auto keyword. Of course you can't just close the resulting stream variable, but the GNU Libstdc++ std::istream destructor does take care of closing the file, so no extra memory management is required anywhere.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

潜移默化 2024-11-24 18:32:44

怎么样:

ifstream * open_ifstream(const string &filename);

What about:

ifstream * open_ifstream(const string &filename);
温暖的光 2024-11-24 18:32:44

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

Couldn't you just use the rdbuf member function to set stream's buffer directly?

乱了心跳 2024-11-24 18:32:44

这是一个不太侵入性的想法:

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

Here's a moderately unintrusive idea:

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

Usage:

std::string filename = getFilename();
windowify(filename);
std::ifstream infile(filename.c_str());
乖乖哒 2024-11-24 18:32:44

我建议一个小的改进:使用 _wopen (或 _wsopen_s)而不是 _wfopen。您将获得一个文件描述符 (int),您可以将其传递给 stdio_filebuf 以代替 FILE*。通过这种方式,您应该避免泄漏任何资源(如 marcin 所指出的)

I would suggest a small improvement: use _wopen (or _wsopen_s) instead of _wfopen. You will get a file descriptor (int) that you can pass to the stdio_filebuf in place of the the FILE*. In this way you should avoid leaking any resource (as pointed out by marcin)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文