从 std::fstream 获取 FILE*
有没有一种(跨平台)方法从 C++ std::fstream 获取 C FILE* 句柄?
我问的原因是因为我的 C++ 库接受 fstream,并且在一个特定函数中我想使用接受 FILE* 的 C 库。
Is there a (cross-platform) way to get a C FILE* handle from a C++ std::fstream ?
The reason I ask is because my C++ library accepts fstreams and in one particular function I'd like to use a C library that accepts a FILE*.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
简短的回答是否定的。
原因是,
std::fstream
不需要使用FILE*
作为其实现的一部分。因此,即使您设法从 std::fstream 对象中提取文件描述符并手动构建 FILE 对象,您也会遇到其他问题,因为现在有两个缓冲对象写入同一个文件描述符。真正的问题是为什么要将
std::fstream
对象转换为FILE*
?虽然我不推荐,但您可以尝试查找
funopen()
。不幸的是,这不是一个 POSIX API(它是一个 BSD 扩展),因此它的可移植性是有问题的。这也可能是为什么我找不到任何人用这样的对象包装
std::stream
的原因。这允许您构建一个 FILE 对象并指定一些将用于完成实际工作的函数。如果您编写适当的函数,您可以让它们从实际打开文件的 std::fstream 对象中读取。
The short answer is no.
The reason, is because the
std::fstream
is not required to use aFILE*
as part of its implementation. So even if you manage to extract file descriptor from thestd::fstream
object and manually build a FILE object, then you will have other problems because you will now have two buffered objects writing to the same file descriptor.The real question is why do you want to convert the
std::fstream
object into aFILE*
?Though I don't recommend it, you could try looking up
funopen()
.Unfortunately, this is not a POSIX API (it's a BSD extension) so its portability is in question. Which is also probably why I can't find anybody that has wrapped a
std::stream
with an object like this.This allows you to build a
FILE
object and specify some functions that will be used to do the actual work. If you write appropriate functions you can get them to read from thestd::fstream
object that actually has the file open.没有一个标准化的方法。我认为这是因为 C++ 标准化小组不想假设文件句柄可以表示为 fd。
大多数平台似乎确实提供了一些非标准的方法来做到这一点。
http://www.ginac.de/~kreckel/fileno/ 提供了很好的文章情况并提供隐藏所有平台特定严重性的代码,至少对于 GCC 来说是这样。考虑到这在海湾合作委员会上是多么恶心,我想如果可能的话我会避免一起做这一切。
There isn't a standardized way. I assume this is because the C++ standardization group didn't want to assume that a file handle can be represented as a fd.
Most platforms do seem to provide some non-standard way to do this.
http://www.ginac.de/~kreckel/fileno/ provides a good writeup of the situation and provides code that hides all the platform specific grossness, at least for GCC. Given how gross this is just on GCC, I think I'd avoid doing this all together if possible.
原文:(
可能不是跨平台,但很简单)
简化 http://www.ginac 中的黑客攻击.de/~kreckel/fileno/ (dvorak 答案),并查看这个 gcc 扩展 http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00069.html#a59f78806603c619eafcd4537c920f859,
我有一个适用于 C++11 之前的
GCC
(至少 4.8)和clang
(至少 3.3)的解决方案:并且可以使用这个,
注意:
stdio_filebuf
未在较新版本的库中使用。static_cast<>()
也有些危险。如果您得到的nullptr
不是正确的类,请使用dynamic_cast<>()
而不是。您可以尝试使用stdio_sync_filebuf
来代替。该类的问题是file()
根本不再可用。限制:(欢迎评论)
我发现在
fprintf
打印到std::ofstream 后进行
,否则上例中“sample2”将出现在“sample1”之前。我不知道是否有比使用 fflush 更好的解决方法。值得注意的是fflush
很重要ofs <<冲洗
没有帮助。无法从
std::stringstream
中提取FILE*,我什至不知道是否可能。 (请参阅下面的更新)。我仍然不知道如何从
std::cerr
等中提取 C 的stderr
,例如在fprintf(stderr, "sample ")
,在这样的假设代码中fprintf(cfile(std::cerr), "sample")
。关于最后一个限制,我发现的唯一解决方法是添加这些重载:
尝试处理 iostringstream
可以使用
fscanf
从 < code>istream 使用fmemopen
,但是如果想将 C 读取和 C++ 读取结合起来,则需要在每次读取后进行大量簿记和更新流的输入位置。我无法将其转换为上面那样的cfile
函数。 (也许在每次读取后不断更新的cfile
class 是可行的方法)。ORIGINAL:
(Probably not cross platform, but simple)
Simplifying the hack in http://www.ginac.de/~kreckel/fileno/ (dvorak answer), and looking at this gcc extension http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00069.html#a59f78806603c619eafcd4537c920f859,
I have this solution that works on
GCC
(4.8 at least) andclang
(3.3 at least) before C++11:and can be used this,
Note: The
stdio_filebuf
is not used in newer versions of the library. Thestatic_cast<>()
is somewhat dangerous too. Use adynamic_cast<>()
instead of if you get anullptr
you need that's not the right class. You can try withstdio_sync_filebuf
instead. Problem with that class is that thefile()
is not available at all anymore.Limitations: (comments are welcome)
I find that it is important to
fflush
afterfprintf
printing tostd::ofstream
, otherwise the "sample2" appears before "sample1" in the example above. I don't know if there is a better workaround for that than usingfflush
. Notablyofs << flush
doesn't help.Cannot extract FILE* from
std::stringstream
, I don't even know if it is possible. (see below for an update).I still don't know how to extract C's
stderr
fromstd::cerr
etc., for example to use infprintf(stderr, "sample")
, in an hypothetical code like thisfprintf(cfile(std::cerr), "sample")
.Regarding the last limitation, the only workaround I found is to add these overloads:
Attempt to handle
iostringstream
It is possible to read with
fscanf
fromistream
usingfmemopen
, but that requires a lot of book keeping and updating the input position of the stream after each read, if one wants to combine C-reads and C++-reads. I wasn't able to convert this into acfile
function like above. (Maybe acfile
class that keeps updating after each read is the way to go).好吧,你可以获取文件描述符 - 我忘记了该方法是 fd() 还是 getfd()。 我使用的实现提供了这样的方法,但我相信语言标准不需要它们 - 标准不应该关心您的平台是否使用文件描述符。
从中,您可以使用fdopen(fd, mode) 获取 FILE*。
但是,我认为标准所需的同步 STDIN/cin、STDOUT/cout 和 STDERR/cerr 的机制不必对您可见。因此,如果您同时使用 fstream 和 FILE*,缓冲可能会让您陷入困境。
另外,如果 fstream 或 FILE 关闭,它们可能会关闭底层 fd,因此您需要确保在关闭之前刷新两者。
Well, you can get the file descriptor - I forget whether the method is fd() or getfd(). The implementations I've used provide such methods, but the language standard doesn't require them, I believe - the standard shouldn't care whether your platform uses fd's for files.
From that, you can use fdopen(fd, mode) to get a FILE*.
However, I think that the mechanisms the standard requires for synching STDIN/cin, STDOUT/cout and STDERR/cerr don't have to be visible to you. So if you're using both the fstream and FILE*, buffering may mess you up.
Also, if either the fstream OR the FILE closes, they'll probably close the underlying fd, so you need to make sure you flush BOTH before closing EITHER.
在 Linux 中执行此操作的另一种方法:
用法,例如:
yet another way to do this in Linux:
Usage, for example:
在单线程 POSIX 应用程序中,您可以轻松地以可移植的方式获取 fd 编号:
如果此代码与打开文件描述符的其他线程竞争,则此方法会在多线程应用程序中中断。
In a single-threaded POSIX application you can easily get the fd number in a portable way:
This method breaks in a multi-threaded application if this code races with other threads opening file descriptors.
有一种方法可以从
fstream
获取文件描述符,然后将其转换为FILE*
(通过fdopen
)。就我个人而言,我认为 FILE* 没有任何必要,但是使用文件描述符,您可以做许多有趣的事情,例如重定向(dup2
)。解决方案:
最后一个字符串适用于 libstdc++。如果您使用其他库,则需要对其进行一些逆向工程。
这个技巧很肮脏,会暴露 fstream 的所有私有和公共成员。如果您想在生产代码中使用它,我建议您使用单个函数
int getFdFromFstream(std::basic_ios<字符>&fstr);
。头文件不得包含 fstream。There is a way to get file descriptor from
fstream
and then convert it toFILE*
(viafdopen
). Personally I don't see any need inFILE*
, but with file descriptor you may do many interesting things such as redirecting (dup2
).Solution:
The last string works for libstdc++. If you are using some other library you will need to reverse-engineer it a bit.
This trick is dirty and will expose all private and public members of fstream. If you would like to use it in your production code I suggest you to create separate
.cpp
and.h
with single functionint getFdFromFstream(std::basic_ios<char>& fstr);
. Header file must not include fstream.当我遇到仅处理文件描述符的 istty() 时,我遇到了这个问题。
在较新版本的 C++ 标准库中(至少自 C++11 以来),alfC 提出的解决方案不再有效,因为一个类已更改为新类。
如果您使用非常旧版本的编译器,旧方法仍然有效。在较新的版本中,您需要使用
std::basic_filebuf<>()
。但这不适用于标准 I/O,例如 std::cout。对于这些,您需要使用 __gnu_cxx::stdio_sync_filebuf<>()。我在
isatty()< 的实现中有一个功能示例/code>
用于此处的 C++ 流。您应该能够删除该文件并在您自己的项目中重复使用它。不过,就您而言,您需要 FILE* 指针,因此只需返回该指针,而不是返回
::isatty(fileno())
。这是模板函数的副本:
现在,您应该问:但是那个详细信息类
our_basic_filebuf
是什么?!?这是一个好问题。事实上,
_M_file
指针受到保护,并且std 中没有
。因此,我创建了一个 shell 类,它可以访问受保护的字段,这样我就可以返回 FILE* 指针。file()
(或fd()
): :basic_filebuf这有点丑陋,但我能想到的最干净的方法是访问
_M_file
字段。I ran in that problem when I was faced with
isatty()
only working on a file descriptor.In newer versions of the C++ standard library (at least since C++11), the solution proposed by alfC does not work anymore because that one class was changed to a new class.
The old method will still work if you use very old versions of the compiler. In newer version, you need to use
std::basic_filebuf<>()
. But that does not work with the standard I/O such asstd::cout
. For those, you need to use__gnu_cxx::stdio_sync_filebuf<>()
.I have a functional example in my implementation of
isatty()
for C++ streams here. You should be able to lift off that one file and reuse it in your own project. In your case, though, you wanted theFILE*
pointer, so just return that instead of the result of::isatty(fileno(<of FILE*>))
.Here is a copy of the template function:
Now, you should be asking: But what is that detail class
our_basic_filebuf
?!?And that's a good question. The fact is that the
_M_file
pointer is protected and there is nofile()
(orfd()
) in thestd::basic_filebuf
. For that reason, I created a shell class which has access to the protected fields and that way I can return theFILE*
pointer.This is somewhat ugly, but cleanest I could think off to gain access to the
_M_file
field.对 Alexis Wilke 想法的修改
它适用于我在 MSVC 2022 和 MSYS2 下
Revision of the idea of Alexis Wilke
It works for me under MSVC 2022 and MSYS2