我希望能够从C ++读取和写入程序。似乎pstream可以完成这项工作,但我发现文档很难理解,还没有找到一个例子。
我已设置以下最低工作示例。这将打开python,反过来(1)打印hello
(2)询问输入,(3)打印hello2
:
#include <iostream>
#include <cstdio>
#include "pstream.h"
using namespace std;
int main(){
std::cout << "start";
redi::pstream proc(R"(python -c "if 1:
print 'hello'
raw_input()
print 'hello2'
")");
std::string line;
//std::cout.flush();
while (std::getline(proc.out(), line)){
std::cout << " " << "stdout: " << line << '\n';
}
std::cout << "end";
return 0;
}
如果我在“ask input”部分注释掉(即#raw_input()
)的情况下执行此操作,我会得到输出:
start stdout: hello
stdout: hello2
end
但是如果我把“询问输入”部分留下(即取消注释raw_input()
),我得到的只是空白,甚至不是start
,而是看起来像是等待输入的程序。 / p>
我的问题是,如何与这个pstream进行交互,如何建立一个小的读写 - 读 - 写会话?为什么该计划甚至没有显示start
或第一个hello
?
修改
我似乎没有取得多大进展。我认为我真的不知道发生了什么。以下是一些评论的进一步尝试。
1)似乎我可以成功输入raw_input
我通过写信给孩子的stderr来证明这一点:
int main(){
cout << "start" <<endl;
redi::pstream proc(R"(python -c "if 1:
import sys
print 'hello'
sys.stdout.flush()
a = raw_input()
sys.stdin.flush()
sys.stderr.write('hello2 '+ a)
sys.stderr.flush()
")");
string line;
getline(proc.out(), line);
cout << line << endl;
proc.write("foo",3).flush();
cout << "end" << endl;
return 0;
}
输出:
start
hello
end
hello2 foo
但如果我再次尝试从stdout读取它会锁定
int main(){
...
a = raw_input()
sys.stdin.flush()
print 'hello2', a
sys.stdout.flush()
")");
...
proc.write("foo",3).flush();
std::getline(proc.out(), line);
cout << line << endl;
...
}
输出
start
hello
2)我无法获得完全可行的方法
int main(){
cout << "start" <<endl;
redi::pstream proc(R"(python -c "if 1:
import sys
print 'hello'
sys.stdout.flush()
a = raw_input()
sys.stdin.flush()
")");
std::streamsize n;
char buf[1024];
while ((n = proc.out().readsome(buf, sizeof(buf))) > 0)
std::cout.write(buf, n).flush();
proc.write("foo",3).flush();
cout << "end" << endl;
return 0;
}
输出
start
end
Traceback (most recent call last):
File "<string>", line 5, in <module>
IOError: [Errno 32] Broken pipe
输出包含Python错误,看起来C ++程序在Python管道仍处于打开状态时完成。
问题:是否有人可以提供有关如何编码此顺序通信的实际示例?
答案 0 :(得分:2)
但如果我离开&#34;请求输入&#34;参与(即未注释的raw_input()),我得到的只是空白,甚至没有开始,而是等待输入的程序。
Python进程 等待来自其stdin的输入,该stdin连接到C ++程序中的管道。如果你没有写入pstream,那么Python进程永远不会收到任何东西。
你没有看到&#34;开始&#34;是Python认为它没有连接到终端,所以它不会打扰每次写入stdout。在Python程序中打印后尝试import sys
然后sys.stdout.flush()
。如果你需要它是交互式的,那么你需要定期刷新,或者将stdout设置为非缓冲模式(我不知道如何在Python中执行此操作)。
您还应该知道,在循环中仅使用getline
将阻止等待更多输入,如果Python进程也阻止等待输入,则会出现死锁。请参阅pstreams home page上的使用示例,其中显示了如何使用readsome()
进行非阻塞读取。这将允许您尽可能多地读取,处理它,然后将响应发送回子进程,以便产生更多输出。
修改强>
我不认为我真正掌握了正在发生的事情。
你的问题不是pstreams或python的问题,你只是没有考虑两个通信进程之间的交互以及每个进程的等待。
获取笔和纸并绘制状态图或某种图表,以显示两个流程的位置以及它们正在等待的内容。
1)似乎我可以成功提供raw_input
是的,但你做错了。 raw_input()
读一行,你不是在写一行,而是写了三个字符"foo"
。那不是一条线。
这意味着python进程不断尝试从其stdin读取。父C ++进程写入三个字符然后退出,运行关闭管道的pstream
析构函数。关闭管道导致Python进程因此得到EOF,所以它停止读取(仅获得三个字符而不是整行)。然后Python进程打印到stderr,它连接到你的终端,因为你没有告诉pstream
将一个管道连接到孩子的stderr,所以你看到了那个输出。 / p>
但如果我再次尝试从stdout读取它会锁定
因为现在父C ++进程没有退出,所以不关闭管道,因此子Python进程不会读取EOF并继续等待更多输入。父C ++进程也等待输入,但这永远不会到来。
如果您想发送一条由raw_input()
读取的行,请写一个换行符!
这很好用,因为它会发送一个换行符,导致Python进程超过raw_input()
行:
cout << "start" <<endl;
redi::pstream proc(R"(python -c "if 1:
import sys
print 'hello'
sys.stdout.flush()
a = raw_input()
print 'hello2', a
sys.stdout.flush()
")");
string line;
getline(proc, line);
cout << line << endl;
proc << "foo" << endl; // write to child FOLLOWED BY NEWLINE!
std::getline(proc, line); // read child's response
cout << line << endl;
cout << "end" << endl;
N.B。您不需要使用proc.out()
,因为您还没有将管道附加到流程中。 stderr,所以它始终从proc.out()
读取。您只需要在阅读两者 stdout和stderr时使用它,您可以使用proc.out()
和proc.err()
来区分它们。
2)我无法获得完全可行的方法
同样,你有同样的问题,你只需要写三个字符,所以Python进程永远等待。 C ++进程也试图读取,所以它也会永远等待。死锁。
如果你通过发送一个换行符来修复它(如上所示)你还有另一个问题:C ++程序运行得如此之快,以至于它会进入while
循环,在Python之前调用readsome
过程甚至开始了。它将在管道中找不到任何内容,因此第一个readsome
调用返回0并退出循环。然后C ++程序进入第二个while
循环,子python进程仍然尚未开始打印任何内容,因此循环也不会读取任何内容并退出。然后整个C ++程序退出,最后Python子程序准备运行并尝试打印&#34; hello&#34;但到那时它的父母已经离开,它无法写入管道。
如果您没有时间阅读第一次时间,那么您需要readsome
继续尝试,因此它等待的时间足以让第一个数据可读。< / p>
对于你的简单程序,你真的不需要readsome
,因为Python进程一次只写一行,所以你可以用getline
来读它。但是,如果它可能会写多行,那么您需要能够继续阅读,直到没有更多数据到来,readsome
可以执行(只有在有可用的数据时才会读取)。但是你还需要一些方法来判断是否还有更多的数据(可能是孩子在发送更多数据之前忙于进行一些计算),或者它是否真的完成了。没有一般的方法可以知道,这取决于子进程正在做什么。也许您需要孩子发送一些哨兵值,例如"---END OF RESPONSE---"
,父母可以寻找知道何时停止尝试阅读更多内容。
出于简单示例的目的,我们假设如果readsome
获得超过4个字节,则会收到整个响应:
cout << "start" <<endl;
redi::pstream proc(R"(python -c "if 1:
import sys
print 'hello'
sys.stdout.flush()
a = raw_input()
sys.stdin.flush()
print 'hello2', a
sys.stdout.flush()
")");
string reply;
streamsize n;
char buf[1024];
while ((n = proc.readsome(buf, sizeof(buf))) != -1)
{
if (n > 0)
reply.append(buf, n);
else
{
// Didn't read anything. Is that a problem?
// Need to try to process the content of 'reply' and see if
// it's what we're expecting, or if it seems to be incomplete.
//
// Let's assume that if we've already read more than 4 characters
// it's a complete response and there's no more to come:
if (reply.length() > 3)
break;
}
}
cout << reply << std::flush;
proc << "foo" << std::endl;
while (getline(proc, reply)) // maybe use readsome again here
cout << reply << std::endl;
cout << "end" << endl;
这会在readsome() != -1
时循环,因此如果它什么都不读则会一直重试,只有在出现错误时才会停止循环。在循环体中,它决定如果没有读取什么该做什么。您需要在此处插入自己的逻辑,这对您尝试做的事情都有意义,但基本上如果readsome()
还没有读取任何内容 ,那么你应该循环并重试。这使得C ++程序等待足够长的时间让Python程序打印出来。
您可能希望将while
循环拆分为一个单独的函数,该函数将整个回复读入std::string
并返回它,以便您可以每次重复使用该函数你想要阅读回复的时间。如果孩子发送了一些标记值,那么该函数很容易编写,因为它只会在每次收到标记字符串时停止。