Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:50:13

0001 // Copyright (c) 2022 Klemens D. Morgenstern
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0004 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0005 #ifndef BOOST_PROCESS_V2_POPEN_HPP
0006 #define BOOST_PROCESS_V2_POPEN_HPP
0007 
0008 #include <boost/process/v2/process.hpp>
0009 #include <boost/process/v2/stdio.hpp>
0010 
0011 #if defined(BOOST_PROCESS_V2_STANDALONE)
0012 #include <asio/connect_pipe.hpp>
0013 #include <asio/readable_pipe.hpp>
0014 #include <asio/writable_pipe.hpp>
0015 #else
0016 #include <boost/asio/connect_pipe.hpp>
0017 #include <boost/asio/readable_pipe.hpp>
0018 #include <boost/asio/writable_pipe.hpp>
0019 #endif
0020 
0021 BOOST_PROCESS_V2_BEGIN_NAMESPACE
0022 
0023 /// A subprocess with automatically assigned pipes.
0024 /** The purpose os the popen is to provide a convenient way 
0025  * to use the stdin & stdout of a process. 
0026  * 
0027  * @code {.cpp}
0028  * popen proc(executor, find_executable("addr2line"), {argv[0]});
0029  * asio::write(proc, asio::buffer("main\n"));
0030  * std::string line;
0031  * asio::read_until(proc, asio::dynamic_buffer(line), '\n');
0032  * @endcode
0033  * 
0034  * 
0035  * Popen can be used as a stream object in other protocols.
0036  */ 
0037 template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
0038 struct basic_popen : basic_process<Executor>
0039 {
0040     /// The executor of the process
0041     using executor_type = Executor;
0042 
0043     /// Rebinds the popen type to another executor.
0044     template <typename Executor1>
0045     struct rebind_executor
0046     {
0047         /// The pipe type when rebound to the specified executor.
0048         typedef basic_popen<Executor1> other;
0049     };
0050 
0051     /// Move construct a popen
0052     basic_popen(basic_popen &&) = default;
0053     /// Move assign a popen
0054     basic_popen& operator=(basic_popen &&) = default;
0055 
0056     /// Move construct a popen and change the executor type.
0057     template<typename Executor1>
0058     basic_popen(basic_popen<Executor1>&& lhs)
0059         : basic_process<Executor>(std::move(lhs)),
0060                 stdin_(std::move(lhs.stdin_)), stdout_(std::move(lhs.stdout_))
0061     {
0062     }
0063 
0064     /// Create a closed process handle
0065     explicit basic_popen(executor_type exec) : basic_process<Executor>{std::move(exec)} {}
0066 
0067     /// Create a closed process handle
0068     template <typename ExecutionContext>
0069     explicit basic_popen(ExecutionContext & context,
0070         typename std::enable_if<
0071             is_convertible<ExecutionContext&,
0072                     BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
0073         : basic_process<Executor>{context}
0074     {
0075     }
0076 
0077     /// Construct a child from a property list and launch it using the default process launcher.
0078     template<typename ... Inits>
0079     explicit basic_popen(
0080             executor_type executor,
0081             const filesystem::path& exe,
0082             std::initializer_list<string_view> args,
0083             Inits&&... inits)
0084             : basic_process<Executor>(executor)
0085     {
0086         this->basic_process<Executor>::operator=(
0087             default_process_launcher()(
0088                     this->get_executor(), exe, args,
0089                     std::forward<Inits>(inits)...,
0090                     process_stdio{stdin_, stdout_}
0091                     ));
0092     }
0093 
0094     /// Construct a child from a property list and launch it using the default process launcher.
0095     template<typename Launcher, typename ... Inits>
0096     explicit basic_popen(
0097             Launcher && launcher,
0098             executor_type executor,
0099             const filesystem::path& exe,
0100             std::initializer_list<string_view> args,
0101             Inits&&... inits)
0102             : basic_process<Executor>(executor)
0103     {
0104         this->basic_process<Executor>::operator=(
0105             std::forward<Launcher>(launcher)(
0106                     this->get_executor(), exe, args,
0107                     std::forward<Inits>(inits)...,
0108                     process_stdio{stdin_, stdout_}
0109                     ));
0110     }
0111 
0112     /// Construct a child from a property list and launch it using the default process launcher.
0113     template<typename Args, typename ... Inits>
0114     explicit basic_popen(
0115             executor_type executor,
0116     const filesystem::path& exe,
0117             Args&& args, Inits&&... inits)
0118             : basic_process<Executor>(executor)
0119     {
0120         this->basic_process<Executor>::operator=(
0121                 default_process_launcher()(
0122                         std::move(executor), exe, args,
0123                         std::forward<Inits>(inits)...,
0124                         process_stdio{stdin_, stdout_}
0125                 ));
0126     }
0127 
0128     /// Construct a child from a property list and launch it using the default process launcher.
0129     template<typename Launcher, typename Args, typename ... Inits>
0130     explicit basic_popen(
0131             Launcher && launcher, 
0132             executor_type executor,
0133             const filesystem::path& exe,
0134             Args&& args, Inits&&... inits)
0135             : basic_process<Executor>(executor)
0136     {
0137         this->basic_process<Executor>::operator=(
0138                 std::forward<Launcher>(launcher)(
0139                         std::move(executor), exe, args,
0140                         std::forward<Inits>(inits)...,
0141                         process_stdio{stdin_, stdout_}
0142                 ));
0143     }
0144 
0145     /// Construct a child from a property list and launch it using the default process launcher.
0146     template<typename ExecutionContext, typename ... Inits>
0147     explicit basic_popen(
0148             ExecutionContext & context,
0149             typename std::enable_if<
0150                 std::is_convertible<ExecutionContext&,
0151                     BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
0152             const filesystem::path&>::type exe,
0153             std::initializer_list<string_view> args,
0154             Inits&&... inits)
0155             : basic_process<Executor>(context)
0156     {
0157         this->basic_process<Executor>::operator=(
0158                 default_process_launcher()(
0159                         this->get_executor(), exe, args,
0160                         std::forward<Inits>(inits)...,
0161                         process_stdio{stdin_, stdout_}
0162                 ));
0163     }
0164 
0165         /// Construct a child from a property list and launch it using the default process launcher.
0166     template<typename Launcher, typename ExecutionContext, typename ... Inits>
0167     explicit basic_popen(
0168             Launcher && launcher, 
0169             ExecutionContext & context,
0170             typename std::enable_if<
0171                 std::is_convertible<ExecutionContext&,
0172                     BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
0173             const filesystem::path&>::type exe,
0174             std::initializer_list<string_view> args,
0175             Inits&&... inits)
0176             : basic_process<Executor>(context)
0177     {
0178         this->basic_process<Executor>::operator=(
0179                 std::forward<Launcher>(launcher)(
0180                         this->get_executor(), exe, args,
0181                         std::forward<Inits>(inits)...,
0182                         process_stdio{stdin_, stdout_}
0183                 ));
0184     }
0185 
0186     /// Construct a child from a property list and launch it using the default process launcher.
0187     template<typename ExecutionContext, typename Args, typename ... Inits>
0188     explicit basic_popen(
0189             ExecutionContext & context,
0190             typename std::enable_if<
0191                 std::is_convertible<ExecutionContext&,
0192                     BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
0193             const filesystem::path&>::type exe,
0194             Args&& args, Inits&&... inits)
0195             : basic_process<Executor>(context)
0196     {
0197         this->basic_process<Executor>::operator=(
0198                 default_process_launcher()(
0199                         this->get_executor(), exe, args,
0200                         std::forward<Inits>(inits)...,
0201                         process_stdio{stdin_, stdout_}
0202                 ));
0203     }
0204 
0205         /// Construct a child from a property list and launch it using the default process launcher.
0206     template<typename Launcher, typename ExecutionContext, typename Args, typename ... Inits>
0207     explicit basic_popen(
0208             Launcher && launcher, 
0209             ExecutionContext & context,
0210             typename std::enable_if<
0211                 std::is_convertible<ExecutionContext&,
0212                     BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
0213             const filesystem::path&>::type exe,
0214             Args&& args, Inits&&... inits)
0215             : basic_process<Executor>(context)
0216     {
0217         this->basic_process<Executor>::operator=(
0218                 std::forward<Launcher>(launcher)(
0219                         this->get_executor(), exe, args,
0220                         std::forward<Inits>(inits)...,
0221                         process_stdio{stdin_, stdout_}
0222                 ));
0223     }
0224 
0225 
0226     /// The type used for stdin on the parent process side.
0227     using stdin_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor>;
0228     /// The type used for stdout on the parent process side.
0229     using stdout_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor>;
0230 
0231     /// Get the stdin pipe.
0232     stdin_type  & get_stdin()  {return stdin_; }
0233     /// Get the stdout pipe.
0234     stdout_type & get_stdout() {return stdout_; }
0235 
0236     /// Get the stdin pipe.
0237     const stdin_type  & get_stdin()  const {return stdin_; }
0238     /// Get the stdout pipe.
0239     const stdout_type & get_stdout() const {return stdout_; }
0240 
0241     /// Write some data to the pipe.
0242     /**
0243      * This function is used to write data to the pipe. The function call will
0244      * block until one or more bytes of the data has been written successfully,
0245      * or until an error occurs.
0246      *
0247      * @param buffers One or more data buffers to be written to the pipe.
0248      *
0249      * @returns The number of bytes written.
0250      *
0251      * @throws boost::system::system_error Thrown on failure. An error code of
0252      * boost::asio::error::eof indicates that the connection was closed by the
0253      * subprocess.
0254      *
0255      * @note The write_some operation may not transmit all of the data to the
0256      * peer. Consider using the @ref write function if you need to ensure that
0257      * all data is written before the blocking operation completes.
0258      *
0259      * @par Example
0260      * To write a single data buffer use the @ref buffer function as follows:
0261      * @code
0262      * pipe.write_some(boost::asio::buffer(data, size));
0263      * @endcode
0264      * See the @ref buffer documentation for information on writing multiple
0265      * buffers in one go, and how to use it with arrays, boost::array or
0266      * std::vector.
0267      */
0268     template <typename ConstBufferSequence>
0269     std::size_t write_some(const ConstBufferSequence& buffers)
0270     {
0271         return stdin_.write_some(buffers);
0272     }
0273 
0274     /// Write some data to the pipe.
0275     /**
0276      * This function is used to write data to the pipe. The function call will
0277      * block until one or more bytes of the data has been written successfully,
0278      * or until an error occurs.
0279      *
0280      * @param buffers One or more data buffers to be written to the pipe.
0281      *
0282      * @param ec Set to indicate what error occurred, if any.
0283      *
0284      * @returns The number of bytes written. Returns 0 if an error occurred.
0285      *
0286      * @note The write_some operation may not transmit all of the data to the
0287      * subprocess. Consider using the @ref write function if you need to ensure that
0288      * all data is written before the blocking operation completes.
0289      */
0290     template <typename ConstBufferSequence>
0291     std::size_t write_some(const ConstBufferSequence& buffers,
0292                            boost::system::error_code& ec)
0293     {
0294         return stdin_.write_some(buffers, ec);
0295     }
0296 
0297     /// Start an asynchronous write.
0298     /**
0299      * This function is used to asynchronously write data to the pipe. It is an
0300      * initiating function for an @ref asynchronous_operation, and always returns
0301      * immediately.
0302      *
0303      * @param buffers One or more data buffers to be written to the pipe.
0304      * Although the buffers object may be copied as necessary, ownership of the
0305      * underlying memory blocks is retained by the caller, which must guarantee
0306      * that they remain valid until the completion handler is called.
0307      *
0308      * @param token The @ref completion_token that will be used to produce a
0309      * completion handler, which will be called when the write completes.
0310      * Potential completion tokens include @ref use_future, @ref use_awaitable,
0311      * @ref yield_context, or a function object with the correct completion
0312      * signature. The function signature of the completion handler must be:
0313      * @code void handler(
0314      *   const boost::system::error_code& error, // Result of operation.
0315      *   std::size_t bytes_transferred // Number of bytes written.
0316      * ); @endcode
0317      * Regardless of whether the asynchronous operation completes immediately or
0318      * not, the completion handler will not be invoked from within this function.
0319      * On immediate completion, invocation of the handler will be performed in a
0320      * manner equivalent to using boost::asio::post().
0321      *
0322      * @par Completion Signature
0323      * @code void(boost::system::error_code, std::size_t) @endcode
0324      *
0325      * @note The write operation may not transmit all of the data to the peer.
0326      * Consider using the @ref async_write function if you need to ensure that all
0327      * data is written before the asynchronous operation completes.
0328      *
0329      * @par Example
0330      * To write a single data buffer use the @ref buffer function as follows:
0331      * @code
0332      * popen.async_write_some(boost::asio::buffer(data, size), handler);
0333      * @endcode
0334      * See the @ref buffer documentation for information on writing multiple
0335      * buffers in one go, and how to use it with arrays, boost::array or
0336      * std::vector.
0337      */
0338     template <typename ConstBufferSequence,
0339             BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
0340                                                     std::size_t)) WriteToken
0341             BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
0342     BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WriteToken,
0343                                        void (boost::system::error_code, std::size_t))
0344     async_write_some(const ConstBufferSequence& buffers,
0345                      BOOST_ASIO_MOVE_ARG(WriteToken) token
0346                      BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
0347     {
0348         return stdin_.async_write_some(buffers, std::forward<WriteToken>(token));
0349     }
0350 
0351 
0352     /// Read some data from the pipe.
0353     /**
0354      * This function is used to read data from the pipe. The function call will
0355      * block until one or more bytes of data has been read successfully, or until
0356      * an error occurs.
0357      *
0358      * @param buffers One or more buffers into which the data will be read.
0359      *
0360      * @returns The number of bytes read.
0361      *
0362      * @throws boost::system::system_error Thrown on failure. An error code of
0363      * boost::asio::error::eof indicates that the connection was closed by the
0364      * peer.
0365      *
0366      * @note The read_some operation may not read all of the requested number of
0367      * bytes. Consider using the @ref read function if you need to ensure that
0368      * the requested amount of data is read before the blocking operation
0369      * completes.
0370      *
0371      * @par Example
0372      * To read into a single data buffer use the @ref buffer function as follows:
0373      * @code
0374      * basic_readable_pipe.read_some(boost::asio::buffer(data, size));
0375      * @endcode
0376      * See the @ref buffer documentation for information on reading into multiple
0377      * buffers in one go, and how to use it with arrays, boost::array or
0378      * std::vector.
0379      */
0380     template <typename MutableBufferSequence>
0381     std::size_t read_some(const MutableBufferSequence& buffers)
0382     {
0383         return stdout_.read_some(buffers);
0384     }
0385 
0386     /// Read some data from the pipe.
0387     /**
0388      * This function is used to read data from the pipe. The function call will
0389      * block until one or more bytes of data has been read successfully, or until
0390      * an error occurs.
0391      *
0392      * @param buffers One or more buffers into which the data will be read.
0393      *
0394      * @param ec Set to indicate what error occurred, if any.
0395      *
0396      * @returns The number of bytes read. Returns 0 if an error occurred.
0397      *
0398      * @note The read_some operation may not read all of the requested number of
0399      * bytes. Consider using the @ref read function if you need to ensure that
0400      * the requested amount of data is read before the blocking operation
0401      * completes.
0402      */
0403     template <typename MutableBufferSequence>
0404     std::size_t read_some(const MutableBufferSequence& buffers,
0405                           boost::system::error_code& ec)
0406     {
0407         return stdout_.read_some(buffers, ec);
0408     }
0409 
0410     /// Start an asynchronous read.
0411     /**
0412      * This function is used to asynchronously read data from the pipe. It is an
0413      * initiating function for an @ref asynchronous_operation, and always returns
0414      * immediately.
0415      *
0416      * @param buffers One or more buffers into which the data will be read.
0417      * Although the buffers object may be copied as necessary, ownership of the
0418      * underlying memory blocks is retained by the caller, which must guarantee
0419      * that they remain valid until the completion handler is called.
0420      *
0421      * @param token The @ref completion_token that will be used to produce a
0422      * completion handler, which will be called when the read completes.
0423      * Potential completion tokens include @ref use_future, @ref use_awaitable,
0424      * @ref yield_context, or a function object with the correct completion
0425      * signature. The function signature of the completion handler must be:
0426      * @code void handler(
0427      *   const boost::system::error_code& error, // Result of operation.
0428      *   std::size_t bytes_transferred // Number of bytes read.
0429      * ); @endcode
0430      * Regardless of whether the asynchronous operation completes immediately or
0431      * not, the completion handler will not be invoked from within this function.
0432      * On immediate completion, invocation of the handler will be performed in a
0433      * manner equivalent to using boost::asio::post().
0434      *
0435      * @par Completion Signature
0436      * @code void(boost::system::error_code, std::size_t) @endcode
0437      *
0438      * @note The read operation may not read all of the requested number of bytes.
0439      * Consider using the @ref async_read function if you need to ensure that the
0440      * requested amount of data is read before the asynchronous operation
0441      * completes.
0442      *
0443      * @par Example
0444      * To read into a single data buffer use the @ref buffer function as follows:
0445      * @code
0446      * basic_readable_pipe.async_read_some(
0447      *     boost::asio::buffer(data, size), handler);
0448      * @endcode
0449      * See the @ref buffer documentation for information on reading into multiple
0450      * buffers in one go, and how to use it with arrays, boost::array or
0451      * std::vector.
0452      */
0453     template <typename MutableBufferSequence,
0454             BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
0455                                                     std::size_t)) ReadToken
0456             BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
0457     BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(ReadToken,
0458                                        void (boost::system::error_code, std::size_t))
0459     async_read_some(const MutableBufferSequence& buffers,
0460                     BOOST_ASIO_MOVE_ARG(ReadToken) token
0461                     BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
0462     {
0463         return stdout_.async_read_some(buffers, std::forward<ReadToken>(token));
0464     }
0465 
0466 
0467 
0468   private:
0469     stdin_type  stdin_ {basic_process<Executor>::get_executor()};
0470     stdout_type stdout_{basic_process<Executor>::get_executor()};
0471 };
0472 
0473 /// A popen object with the default  executor.
0474 using popen = basic_popen<>;
0475 
0476 BOOST_PROCESS_V2_END_NAMESPACE
0477 
0478 #endif //BOOST_PROCESS_V2_POPEN_HPP