Silicium
run_process.hpp
Go to the documentation of this file.
1 #ifndef SILICIUM_RUN_PROCESS_HPP
2 #define SILICIUM_RUN_PROCESS_HPP
3 
5 #include <silicium/write.hpp>
6 #include <silicium/sink/multi_sink.hpp>
7 #include <boost/range/algorithm/transform.hpp>
8 
9 namespace Si
10 {
11  template <class T>
12  bool extract(T &destination, optional<T> &&source)
13  {
14  if (source)
15  {
16  destination = std::forward<T>(*source);
17  return true;
18  }
19  return false;
20  }
21 }
22 
23 #define SILICIUM_HAS_RUN_PROCESS SILICIUM_HAS_EXCEPTIONS
24 
25 #if SILICIUM_HAS_RUN_PROCESS
26 #include <future>
27 
28 namespace Si
29 {
30  inline int run_process(process_parameters const &parameters)
31  {
32  async_process_parameters async_parameters;
33  if (!extract(async_parameters.executable, absolute_path::create(parameters.executable)))
34  {
35  throw std::invalid_argument("a process can only be started with an absolute path to the executable");
36  }
37  if (!extract(async_parameters.current_path, absolute_path::create(parameters.current_path)))
38  {
39  throw std::invalid_argument("a process can only be started with an absolute path as the working directory");
40  }
41  boost::range::transform(parameters.arguments, std::back_inserter(async_parameters.arguments), [](std::string const &argument)
42  {
43  return to_os_string(argument);
44  });
45  auto input = make_pipe().move_value();
46  auto std_output = make_pipe().move_value();
47  auto std_error = make_pipe().move_value();
48  async_process process =
49  launch_process(async_parameters, input.read.handle, std_output.write.handle, std_error.write.handle, {}, environment_inheritance::inherit).move_value();
50 
51  boost::asio::io_service io;
52 
53  auto std_output_consumer = make_multi_sink<char, success>([&parameters]()
54  {
55  return make_iterator_range(&parameters.out, &parameters.out + (parameters.out != nullptr));
56  });
57  experimental::read_from_anonymous_pipe(io, std_output_consumer, std::move(std_output.read));
58 
59  auto std_error_consumer = make_multi_sink<char, success>([&parameters]()
60  {
61  return make_iterator_range(&parameters.err, &parameters.err + (parameters.err != nullptr));
62  });
63  experimental::read_from_anonymous_pipe(io, std_error_consumer, std::move(std_error.read));
64 
65  input.read.close();
66  std_output.write.close();
67  std_error.write.close();
68 
69  auto copy_input = std::async(std::launch::async, [&input, &parameters]()
70  {
71  if (!parameters.in)
72  {
73  return;
74  }
75  for (;;)
76  {
77  optional<char> const c = Si::get(*parameters.in);
78  if (!c)
79  {
80  break;
81  }
82  error_or<size_t> written = write(input.write.handle, make_memory_range(&*c, 1));
83  if (written.is_error())
84  {
85  //process must have exited
86  break;
87  }
88  assert(written.get() == 1);
89  }
90  input.write.close();
91  });
92 
93  io.run();
94  copy_input.get();
95 
96  return process.wait_for_exit().get();
97  }
98 
99  inline int run_process(
100  boost::filesystem::path executable,
101  std::vector<std::string> arguments,
102  boost::filesystem::path current_path,
103  Si::sink<char, success> &output)
104  {
105  Si::process_parameters parameters;
106  parameters.executable = std::move(executable);
107  parameters.arguments = std::move(arguments);
108  parameters.current_path = std::move(current_path);
109  parameters.out = &output;
110  return Si::run_process(parameters);
111  }
112 }
113 #endif
114 
115 #endif
std::remove_reference< T >::type && move(T &&ref)
Definition: move.hpp:10
std::vector< std::string > arguments
the values for the child's argv[1...]
Definition: process_parameters.hpp:18
bool extract(T &destination, optional< T > &&source)
Definition: run_process.hpp:12
boost::filesystem::path executable
Definition: process_parameters.hpp:15
sink< char, success > * out
stdout of the child process will be written to this sink. When nullptr, output is discarded...
Definition: process_parameters.hpp:24
Definition: absolute_path.hpp:19
Definition: optional.hpp:39
Definition: process_parameters.hpp:13
boost::filesystem::path current_path
must be an existing path, otherwise the child cannot launch properly
Definition: process_parameters.hpp:21
static SILICIUM_USE_RESULT optional< absolute_path > create(boost::filesystem::path const &maybe_absolute)
Definition: absolute_path.hpp:103
Value get(error_or< Value, Error > &&value)
Definition: error_or.hpp:479
SILICIUM_USE_RESULT error_or< std::size_t > write(native_file_descriptor file, memory_range data)
Definition: write.hpp:13
BOOST_CONSTEXPR auto make_iterator_range(Iterator1 &&begin, Iterator2 &&end) -> iterator_range< typename std::decay< Iterator1 >::type >
Definition: iterator_range.hpp:142
auto make_memory_range(Byte *begin, Byte *end) -> iterator_range< DestType * >
Definition: memory_range.hpp:27