gpt4 book ai didi

c++ - 用上游包装命令行程序

转载 作者:行者123 更新时间:2023-12-01 14:50:48 24 4
gpt4 key购买 nike

我希望能够从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;
}

如果我在注释掉“询问输入”部分的情况下运行它(即 #raw_input()),我将得到输出:
start stdout: hello
stdout: hello2
end

但是,如果我将“询问输入”部分留在(即未注释的 raw_input())中,那么我得到的只是空白,甚至不是 start,而是看起来像一个等待输入的程序。

我的问题是,一个人如何与该pstream交互,一个人如何建立一点读写读写 session ?为什么程序甚至不显示 start或第一个 hello

编辑:
我似乎没有太大的进步。我认为我不是真的了解发生了什么。这是一些进一步的尝试和评论。

1)看来我可以成功地输入raw_input ,我通过写给 child 的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

但是如果我再次尝试从标准输出读取信息,它将锁定
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错误,似乎在Python管道仍处于打开状态时C++程序已完成。

问题:谁能提供一个有效的示例说明如何对这种顺序通信进行编码?

最佳答案

But if I leave the "ask input" part in (i.e. uncommented raw_input()), all I get is blank, not even start, but rather what seems like a program waiting for input.



Python进程正在等待来自其标准输入的输入,该标准输入已连接到C++程序中的管道。如果您不写入pstream,那么Python进程将永远不会收到任何东西。

您没有看到“开始”的原因是Python认为它没有连接到终端,因此它不会打扰每次写入标准输出的操作。在Python程序中打印后,尝试 import sys,然后尝试 sys.stdout.flush()。如果您需要它是交互式的,则需要定期刷新,或将stdout设置为非缓冲模式(我不知道如何在Python中做到这一点)。

您还应该意识到,仅在循环中使用 getline会阻止等待更多的输入,并且如果Python进程也阻止了等待输入,则您将陷入死锁。请参阅 pstreams home page上的用法示例,其中显示了如何将 readsome()用于非阻塞读取。这样一来,您便可以阅读尽可能多的内容,进行处理,然后将响应发送回子进程,从而产生更多的输出。

编辑:

I don't think I really grasp what is going on.



您的问题并不是pstreams或python真正的问题,您只是没有考虑两个通信进程之间的交互以及每个进程都在等待什么。

获得一支笔和纸,并绘制状态图或某种图表,以显示两个过程必须到达的位置以及它们在等待什么。

1) It seems like I can successfully feed raw_input



是的,但是您做错了。 raw_input()读一行,不是写一行,而是写三个字符 "foo"。那不是一行。

这意味着python进程不断尝试从其stdin中读取。父C++进程将写入三个字符,然后退出,运行 pstream析构函数以关闭管道。关闭管道会导致Python进程获得EOF,因此它将停止读取(仅获得三个字符而不是整行之后)。然后,Python进程将打印到连接到终端的stderr,因为您没有告诉 pstream将管道连接到 child 的stderr,因此您看到了输出。

But it locks if I try to read from the stdout again



因为现在父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) I can't get the readsome approach to work at all



同样,您遇到的问题是只写三个字符,因此Python进程将永远等待。 C++进程也在尝试读取,因此也将永远等待。僵局。

如果通过发送换行符来解决该问题(如上所示),则会遇到另一个问题:C++程序将运行得如此之快,以至于它甚至在Python进程启动之前就进入了调用 whilereadsome循环。它将在管道中找不到任何内容,因此第一个 readsome调用返回0,然后退出循环。然后,C++程序进入第二个 while循环,并且子python进程仍尚未开始打印任何内容,因此该循环也不会读取任何内容并退出。然后,整个C++程序退出,最后Python子级准备就绪,可以运行并尝试打印“hello”,但到那时它的父级就消失了,无法将其写入管道。

您需要使用 readsome来继续尝试是否在第一次调用it_时没有什么要读取的内容,因此它需要等待足够长的时间才能读取第一个数据。

对于您的简单程序,您实际上并不需要 readsome,因为Python进程一次只写一行,因此您可以使用 getline读取它。但是,如果它可能写多行,则您需要能够继续读取,直到没有更多数据可以使用 readsome为止(仅当有可用数据时才读取)。但是,您还需要某种方法来判断是否还会有更多数据(可能是 child 在发送更多数据之前正忙于进行一些计算)还是真的完成了。没有一般的方法可以知道,这取决于子进程在做什么。也许您需要 child 发送一些哨兵值,例如 "---END OF RESPONSE---", parent 可以寻找该值以了解何时停止尝试阅读更多内容。

出于简单示例的目的,让我们假设,如果 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并将其返回,以便您每次想要读取响应时都可以重用该函数。如果 child 发送一些哨兵值,该函数将易于编写,因为它每次接收到哨兵字符串时都会停止。

关于c++ - 用上游包装命令行程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35204550/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com