接口是静态类型和可变参数存在问题。[¹]
一般来说,各种关键字参数不能保证是兼容的类型,所以三元像cond? keyword1 : keyword2
会由于类型不匹配而很快崩溃。
此外,有时您会想要动态添加/删除选项。
在我自己的生产代码中,我经常选择传统的控制流和一些代码重复,比如
if (condition)
child = bp::child(/* params 1st style */);
else
child = bp::child(/* params 2nd style */);
当然,如果有很多选择,这会导致难以维护的组合爆炸。您可以使用更复杂的可变参数 lambda 系统来组合关键字参数来欺骗它。
boost::asio::io_service ios;
boost::asio::deadline_timer deadline(ios);
bp::group process_group;
bp::async_pipe input(ios);
std::future<std::string> output, error;
if (_working_directory.empty())
_working_directory = ".";
auto on_exit = [this, &deadline](int exit, std::error_code ec) {
if (!_command_timed_out) {
_exit_code = exit;
}
deadline.cancel();
if (ec) log(LOG_WARNING) << "Child process returned " << ec.message();
else log(LOG_DEBUG) << "Child process returned";
};
auto launcher = [](auto&&... args) { return bp::child(std::forward<decltype(args)>(args)..., bp::posix::fd.restrict_inherit()); };
auto redirect_out = [&](auto f) {
return [&](auto&&... args) {
if (_discard_output) {
if (_redirected_output_fd != -1 && !_redirected_output_fname.empty()) {
log(LOG_ERR) << "Ignoring output redirection with set_discard_output. This is a bug.";
}
return f(std::forward<decltype(args)>(args)..., bp::std_out > bp::null, bp::std_err > bp::null);
}
if (_redirected_output_fd != -1 && !_redirected_output_fname.empty()) {
log(LOG_WARNING) << "Conflicting output redirection, ignoring filename with descriptor";
}
if (_redirected_output_fd != -1) {
return f(std::forward<decltype(args)>(args)..., bp::posix::fd.bind(1, _redirected_output_fd), bp::std_err > error);
}
return _redirected_output_fname.empty()
? f(std::forward<decltype(args)>(args)..., bp::std_out > output, bp::std_err > error)
: f(std::forward<decltype(args)>(args)..., bp::std_out > _redirected_output_fname, bp::std_err > error);
};
};
bp::environment bp_env = boost::this_process::environment();
for (auto& p : _env)
bp_env[p.first] = p.second;
auto c = redirect_out(launcher)(_command_path, _args,
process_group,
bp::std_in < input,
bp::start_dir(_working_directory),
bp_env,
ios, bp::on_exit(on_exit)
);
if (_time_constraint) {
deadline.expires_from_now(*_time_constraint);
deadline.async_wait([&](boost::system::error_code ec) {
if (ec != boost::asio::error::operation_aborted) {
if (ec) {
log(LOG_WARNING) << "Unexpected condition in CommandRunner deadline: " << ec.message();
}
_command_timed_out = true;
_exit_code = 1;
::killpg(process_group.native_handle(), SIGTERM);
deadline.expires_from_now(3s); // grace time
deadline.async_wait([&](boost::system::error_code ec) { if (!ec) process_group.terminate(); }); // timed out
}
});
}
boost::asio::async_write(input, bp::buffer(_stdin_data), [&input](auto ec, auto bytes_written){
if (ec) {
log(LOG_WARNING) << "Standard input rejected: " << ec.message() << " after " << bytes_written << " bytes written";
}
may_fail([&] { input.close(); });
});
ios.run();
if (output.valid()) _stdout_str = output.get();
if (error.valid()) _stderr_str = error.get();
// make sure no grand children survive
if (process_group && process_group.joinable() && !process_group.wait_for(1s))
process_group.terminate();
当然,您应该添加异常处理并适应您的需求。
[¹] 不要让我开始。对我来说,这是过度设计的错误。在存在 process-exec 开销的情况下,没有性能参数可以弥补增加的复杂性和无数错误 [我多年来见过一些]。我不知道如何使用 Boost Process 做一些重要的事情。
当然,图书馆仍然有用,所以我会限制我的咆哮:) 我觉得今天有一些因果报应要燃烧