【问题标题】:Why does _popen work here, but boost::process does not?为什么 _popen 在这里可以工作,而 boost::process 却不行?
【发布时间】:2018-03-27 10:04:02
【问题描述】:

我在 windows 上使用 _popen 有以下工作代码,

m_pGNUPlot = _popen("/gnuplot/bin/gnuplot.exe", "w");
fprintf(m_pGNUPlot, "set term win\n");
fprintf(m_pGNUPlot, "set term pngcairo\n"); 
fprintf(m_pGNUPlot, "plot \"\Data.txt\" using 1:2 notitle\n"); 
fprintf(m_pGNUPlot, "set output \"\Out.png\"\n");
fprintf(m_pGNUPlot, "replot\n");
fflush(m_pGNUPlot);

但是这样做的问题是cmd窗口不断弹出,没有办法阻止(Link) 所以,我在 boost::process 中写了等价的代码

bp::pipe m_Write;
bp::environment env = boost::this_process::environment();
m_Plot = new bp::child("/gnuplot/bin/gnuplot.exe", bp::std_in < m_Write, env, boost::process::windows::hide);
m_Write.write("set term win\n", sizeof(char)*14);
m_Write.write("set term pngcairo\n", sizeof(char) * 19);    
m_Write("plot \"\Data.txt\" using 1:2 notitle\n", sizeof(char)*35);
m_Write("set output \"\Out.png\"\n", sizeof(char)*22);
m_Write.write("replot\n", sizeof(char) * 8);

所以,我的问题是 - 这两个代码 sn-ps 是否等效?如果是这样,为什么第二个不起作用?

【问题讨论】:

  • 您的字符数错误 - 一个(\n 仅在字符上)。您还需要删除\Data.txt\Out.png 前面的反斜杠
  • 应该(并且可以)在编译时或运行时计算硬编码大小是一个非常糟糕的主意。

标签: c++ boost gnuplot popen boost-process


【解决方案1】:

我没有windows,所以在我的linux盒子上测试了一下,稍微简化一下:

#include <boost/process.hpp>
#include <iostream>

namespace bp = boost::process;

int main() {
    bp::opstream m_Write;
    boost::filesystem::path program("/usr/bin/gnuplot");
    bp::child m_Plot(program, bp::std_in = m_Write);

    m_Write << "set term png\n";
    m_Write << "set output \"Out.png\"\n";
    m_Write << "plot \"Data.txt\" using 1:2 notitle\n";
    m_Write.flush();
    m_Write.pipe().close();

    m_Plot.wait();
    std::cout << "Done, exit code: " << m_Plot.exit_code() << "\n";
}

打印:

Done, exit code: 0

并从simplistic data:创建了这个漂亮的图像

窗口

在 Windows 上,利用 Boost Filesystem 的 path 的强大功能来创建路径:

boost::filesystem::path program("C:\\gnuplot\\bin\\gnuplot.exe");

其他说明

如果整个脚本确实是固定的,请考虑使用原始文字:

m_Write << R"(set term png
    set output "Out.png"
    plot "Data.txt" using 1:2 notitle)" << std::flush;
m_Write.pipe().close();

【讨论】:

    【解决方案2】:

    是的,谢谢你! Boost 功能强大,但缺乏教程和示例使其难以入门。

    是的,所以我的最终工作代码 -

    bp::opstream m_Write; //output stream to pipe   
    boost::filesystem::path program("/gnuplot/bin/gnuplot.exe");
    m_Plot = new bp::child(program, bp::std_in = m_Write, bp::windows::hide); //this solves the problem with _popen
    m_Write << "set term png\n";
    m_Write << "set term pngcairo\n"; 
    m_Write << "set output \"" + ToPosixPath(sPath) + "\"\n"; //Notice how this works with std::string :)
    m_Write << "plot  \"" + CreateTemp(X, Y) + "\" using 1:2 notitle\n";
    m_Write << "exit\n";
    m_Write.flush();
    m_Write.pipe().close();
    
    m_Plot->wait(); //boost doc states "The call to wait is necessary, to obtain it and tell the operating system, that no one is waiting for the process anymore."
    delete m_Plot;
    

    几点-

    • 在 windows 中支持管道的 exe 在 gnuplot.exe 本身中,而在 linux 中有两个 - gnuplot.exe 和 pgnuplot.exe。

    • 请务必在 GUI 中测试您的脚本,此代码会静默失败!返回码将为 0。

    【讨论】:

    • 使用流,最好避免+进行字符串拼接("a" + "b" 不会像你想的那样,构造临时字符串是浪费)
    • 不要使用newdelete
    • 如果您有 C++14,请使用 std::quoted,这样包含引号的名称才能正常工作
    • 所以,我认为这是一个不错的版本:paste.ubuntu.com/p/p5XQBsxs67 没有具有 Boost Process 的在线 MSVC 编译器,但这里是文件系统部分:rextester.com/VMCOT78832(注意引号并创建临时路径)
    • 感谢您的宝贵时间!我使用 new 和 delete,因为声明在类头中,但我希望它在构造函数中实例化。那么有没有更好的方法呢?
    猜你喜欢
    • 2011-07-20
    • 2021-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多