我正在使用boost::process调用外部程序-外部程序通过stdin读取输入,并写入stdout和stderr。外部程序如下(需要一个参数-调试文件的路径)
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
int main(int argc, char** argv)
{
try
{
if (argc != 2)
{
throw std::logic_error("Expected two arguments");
}
std::ofstream ofs(argv[1]);
std::vector<std::string> someTestInput;
ofs << "Starting program..." << std::endl;
// Read from cin
{
ofs << "Reading from cin..." << std::endl;
std::string input;
while (std::getline(std::cin, input))
{
ofs << "Received from cin: " << input << std::endl;
someTestInput.emplace_back(input);
}
ofs << "Finished receiving from cin..." << std::endl;
}
// Error if nothing has been input
if (someTestInput.empty())
{
throw std::logic_error("Expected some input and received nothing...");
}
ofs << "Writing to cout..." << std::endl;
// Write to cout
for (const auto& output : someTestInput)
{
std::cout << output << 'n';
}
ofs << "Finished!n";
}
catch (std::exception& e)
{
std::cerr << "Error caught: " << e.what() << 'n';
return 1;
}
return 0;
}
调用方需要2个以上的参数,其中一个是指向外部程序的路径,其余参数作为参数传递给外部程序。
它在等待进程退出时挂起,外部程序似乎在等待来自stdin的EOF。
#include <memory>
#include <vector>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>
int main(int argc, char** argv)
{
try
{
if (argc < 2)
{
throw std::logic_error("Expecting at least 2 arguments...");
}
std::vector<std::string> args;
for (int i = 1; i < argc; ++i)
{
args.emplace_back(argv[i]);
}
std::cout << "Creating stdout, stderr pipes...n";
// Create pipes for stdout, stderr
boost::process::pipe pstdout = boost::process::create_pipe();
boost::process::pipe pstderr = boost::process::create_pipe();
std::cout << "Mapping pipes to sources...n";
// Map pipe source from stdout and stderr to sources
boost::iostreams::file_descriptor_source sourcestdout(pstdout.source, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source sourcestderr(pstderr.source, boost::iostreams::close_handle);
std::cout << "Setting up streams for the sources...n";
// And set up streams for the sources
boost::iostreams::stream<boost::iostreams::file_descriptor_source> istdout(sourcestdout);
boost::iostreams::stream<boost::iostreams::file_descriptor_source> istderr(sourcestderr);
std::unique_ptr<boost::process::child> p;
// Want to check for process result, but also need to ensure stdin handle is closed properly,
// so place everything in separate scope
{
std::cout << "Mapping pipes to sinks...n";
// Map pipe sink from stdout and stderr to sinks
boost::iostreams::file_descriptor_sink sinkstdout(pstdout.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_sink sinkstderr(pstderr.sink, boost::iostreams::close_handle);
std::cout << "Creating stdin pipe, mapping to source and sink...n";
boost::process::pipe pstdin = boost::process::create_pipe();
// For stdin, map pipe to source and sink as before - want it to close on exiting this scope
boost::iostreams::file_descriptor_sink sinkstdin(pstdin.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source sourcestdin(pstdin.source, boost::iostreams::close_handle);
boost::iostreams::stream<boost::iostreams::file_descriptor_sink> ostdin(sinkstdin);
std::cout << "Calling process... n";
// Call process
p = std::unique_ptr<boost::process::child>(new boost::process::child(boost::process::execute(
boost::process::initializers::set_args(args),
boost::process::initializers::throw_on_error(),
boost::process::initializers::bind_stdout(sinkstdout),
boost::process::initializers::bind_stderr(sinkstderr),
boost::process::initializers::bind_stdin(sourcestdin)
)));
std::cout << "Sending test data...n";
// Send some test data to cin - comment out the below to test for error case
ostdin << "Test Input 1n";
ostdin << "Somen";
ostdin << "Usefuln";
ostdin << "Datan";
std::cout << "Test data sent, exiting scope...n";
}
std::cout << "Check if process has exited...n";
// Check if process has exited OK - if not, report errors
if (boost::process::wait_for_exit(*p))
{
std::cout << "Has not exited OK, reporting problems...n";
// Gather output from stderr
std::string error;
while (std::getline(istderr, error))
{
std::cout << "Error: " << error << 'n';
}
throw std::logic_error("Problem executing TestProgram...");
}
std::cout << "Exited OK, here is output from the callee...n";
// Gather the output
std::string output;
while (std::getline(istdout, output))
{
std::cout << output << 'n';
}
}
catch (std::exception& e)
{
std::cerr << "Error: " << e.what() << 'n';
return 1;
}
}
我的印象是,将我的stdin管道和相关的源/汇放在一个范围内可以保证它们是关闭的,因此可以发送EOF。
同样的代码在Windows(VS2013,boost_1_53)下也能完美运行。
我使用的是boost_1_53,助推过程0.5,gcc 4.8.2。
这不会发生,因为子进程中仍有一个管道句柄打开;只有当您显式设置posix时,它才会在posix上关闭(在windows上,它是自动完成的)。所以你需要添加这样的东西:
#if defined (BOOST_POSIX_API)
fcntl(pstdout.sink, F_SETFD, FD_CLOEXEC);
fcntl(pstderr.sink, F_SETFD, FD_CLOEXEC);
#endif
然而,我建议使用boost.asio,异步等待子流程的退出并关闭那里的管道。
仅供参考:我已经研究过助推过程0.6,它有一个不同的界面,但让asio的东西变得更容易。这将有望在10月/11月进行审查,因此它可能很快成为一个官方的助推库。它目前处于测试阶段,所以你可能想看看它。