使用C++读取进程自身的标准输出 (stdout)

4
考虑到我们有一个名为some_function的函数,它将结果打印到stdout而不是返回结果。改变其定义超出了我们的范围,并且没有其他选择。我们只能从stdout中读取结果。所以问题来了。
如何在C++程序本身中读取stdout
可以获取pid,我搜索过是否可以获取相同程序的fd,但是我找不到任何信息。
#include <unistd.h>
#include <sys/types.h>
#include <iostream>
void some_function(){
    std::cout<<"Hello World";
}

int main(){


    int pid = ::getpid();
    string s = //What to write here.

    cout<<"Printing";

    some_function(); //This function prints "Hello World" to screen

    cout<<s; //"PrintingHello World"
    return  0;
}

如何将管道连接到同一进程,即不创建子进程。

有些人可能会想要创建子进程并在其中调用some_function,以便能够在父进程中读取其stdout,但是不行,some_function依赖于调用它的进程,因此我们想要调用它的正是那个进程,而不是创建子进程。


我认为简单的答案是否定的。但是你在考虑什么情况? - Arndt Jonasson
@ArndtJonasson 我已经在上面添加了一段代码示例。 - moghya
1
查找 pipedup2 - Ed Heal
1
我认为如果输出仅限于stdout(文件描述符1),并且您需要捕获它,或者您可以控制代码写入的流,那么这是一个重要的区别。介于两者之间的情况是,如果代码使用std :: cout,并且您可以将其重定向到除文件描述符1之外的其他内容。 - Arndt Jonasson
相关:如何在C中从stdout读取 - Gabriel Staples
显示剩余9条评论
2个回答

6

这并不难做到,但在我看来这是一种取巧的方式,并且它不能用于多线程程序:

// make a temp file to store the function's stdout
int newStdOut = mkstemp( "/tmp/stdout.XXXXXXX" );

// save the original stdout
int tmpStdOut = dup( STDOUT_FILENO );

// clear stdout
fflush( stdout );

// now point the stdout file descriptor to the file
dup2( newStdOut, STDOUT_FILENO );

// call the function we want to collect the stdout from
some_function();

// make sure stdout is empty
fflush( stdout );

// restore original stdout
dup2( tmpStdOut, STDOUT_FILENO );

// the tmp file now contains whatever some_function() wrote to stdout

错误检查、正确的头文件、同步C的stdout和C++的cout,以及读取和清理临时文件等操作需要自己完成... ;-)

请注意,您不能安全地使用管道-函数可以写入足以填满管道的数据,而由于已调用该函数,因此无法从管道中读取。


非常感谢,我会完成代码并在这里发布结果 :) - moghya
如果你有一个事件循环,你可以使用管道。 - Basile Starynkevitch

3
如何在C++程序中读取自身的stdout?
很少有理由这样做,这通常是设计上的一个错误(但并非总是如此)。
要注意一个重要的事情(至少在单线程程序中)。如果你的程序既从其“stdout”中读取,又像通常一样写入它,那么它可能会陷入死锁:无法读取,因此不会到达任何输出例程,(或者无法写入,因为管道已满)。
因此,既读取又写入同一物件(实际上是同一pipe(7)的两个端点)的程序应该使用一些多路复用调用,例如poll(2)。请参见此处
一旦你了解了这一点,你就会有一些事件循环。在此之前,你需要使用pipe(2)(和dup2(2))创建一个pipe(7)
然而,在一些signal(7)处理中,自我管道是一件好事(请参见signal-safety(7)))。这个技巧甚至在Qt Unix信号处理中得到推荐。 阅读更多关于Unix系统编程的内容,例如ALP或一些更新的书籍。还要阅读intro(2)syscalls(2)
我已经查找了pipe,它需要fd。
错误。请仔细阅读pipe(2);成功时,它会填充两个文件描述符的数组。当然它也可能失败(参见errno(3)perror(3)strerror(3))。
也许您只需要使用popen(3)std::ostringstreamopen_memstream(3)

考虑到我们有一个some_function函数,它将结果打印到标准输出而不是返回它。更改其定义超出了我们的范围,也没有替代方法。

如果some_function是您的代码,或者是一些自由软件,您可以并且可能应该改进它以在某个地方给出结果......

非常感谢,我会尽力想出点什么。 - moghya
在编码之前,你真的应该花几天或几周的时间阅读更多的内容。ALP有点老了,但可以免费下载。并且编辑你的问题以更好地激励它(很可能你非常困惑,你的问题很可能是一个XY问题)。 - Basile Starynkevitch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接