2

I have the following working code using _popen, on windows,

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);

But the problem with this is that cmd window keeps poping up, and there is no way to prevent that (Link) So, I write the equivalent code in 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);

So, my question is - are the two code snippets equivalent? And if so, why might the second one not work?

  • Your char counts are wrong - one off (\n is only on char). You also need to remove the backslashes infront of `\Data.txt` and `\Out.png` – Mihayl Mar 27 '18 at 11:02
  • 1
    Very bad idea to hard code sizes when it should (and could) be computed either at compile time or at run-time. – Phil1970 Mar 28 '18 at 00:05

2 Answers2

3

I don't have windows, so I tested it on my linux box, slightly simplified:

#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";
}

Prints:

Done, exit code: 0

And created this nice image from simplistic data:

Windows

On windows, leverage the power of Boost Filesystem's path to do the path:

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

Other Notes

If the whole script is, indeed, fixed, consider using a raw literal:

m_Write << R"(set term png
    set output "Out.png"
    plot "Data.txt" using 1:2 notitle)" << std::flush;
m_Write.pipe().close();
sehe
  • 328,274
  • 43
  • 416
  • 565
1

Yes, thank you sehe! Boost is powerful, but lack of tutorials and examples make it hard to get started with.

Yes, so the final working code for me -

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;

Some points-

  • In windows the exe that supports pipes in gnuplot.exe itself, whereas in linux there are two - gnuplot.exe and pgnuplot.exe.

  • Be sure to test your scripts in the GUI, this code fails silently! Return code will be 0.

  • Using the stream, it's strictly better to avoid `+` for string concatenation ("a" + "b" doesn't do what you think, and constructing temporary strings is wasteful) – sehe Mar 28 '18 at 10:59
  • Don't use `new` and `delete`. – sehe Mar 28 '18 at 11:00
  • Use [`std::quoted`](http://en.cppreference.com/w/cpp/io/manip/quoted) if you have C++14, so that names containing quotes will work correctly – sehe Mar 28 '18 at 11:03
  • So, this is a version that I think would be good: https://paste.ubuntu.com/p/p5XQBsxs67/ There's no online MSVC compiler that has Boost Process, but here's the filesystem part: http://rextester.com/VMCOT78832 (Note the quotes and creating temporary path) – sehe Mar 28 '18 at 12:21
  • Thank you for your time! I use new and delete, because the declaration is in the class header, but I want it to instantiate inside the constructor. Any better way to do that, then? – Vivek Subramanian Mar 29 '18 at 06:21
  • Yes. Instantiation is in the constructor no matter what you do. [Use a member initializer](https://paste.ubuntu.com/p/rjQt2yQCss/), better yet, don't expose Boost Process at all! [no need to even include the headers in the interface](https://paste.ubuntu.com/p/p2HQthy2cZ/) – sehe Mar 29 '18 at 07:25
  • If for some much more involved reason you /need/ dynamic allocation (i.e. when you need to have the lifetime of the `bp::child` independent of any other object or function), you'd still always use a smart pointer, so [You'd Still Not Use `new` or `delete`](https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new?rq=1) – sehe Mar 29 '18 at 07:27