PrevUpHomeNext

boost::process and asio pipe


> Start
> asio pipe
> asio::connect_pipe
> pipe c++ example
> boost::process
> boost::process c++ example 1
> boost::process c++ example 2
> boost::process c++ example 3
> Back: Home

https://www.boost.org/library/latest/process

https://www.boost.org/library/latest/asio

https://think-async.com/Asio

asio pipe

boost asio has two kind of pipe:

asio::readable_pipe is a pipe end that has defined methods .read_some and .async_read_some, it does not define write methods.
asio::writable_pipe is a pipe end that has defined methods .write_some and .async_write_some, it does not define read methods.

So we can read from the asio::readable_pipe object, but we can not write to it ;
and write to the asio::writable_pipe object, but we can not read from it.

asio::connect_pipe

We can connect a readable-pipe and a writable-pipe with asio::connect_pipe :

asio::connect_pipe(readable_pipe, writable_pipe);

pipe c++ example

Pipe game: a pipe c++ example.

jamfile: jamroot

Filename: jamroot

project
:
	default-build
		<cxxstd>26
;

exe bpp-1 : bpp-1.cpp ;

c++ file

The write-pipe end must be closed after writing and before reading read-pipe end.

Filename: bpp-1.cpp

#include <boost/asio.hpp>
#include <iostream>

int main()
{
	try
	{
		boost::asio::thread_pool pool{32};
		boost::asio::readable_pipe rp{pool.get_executor()};
		boost::asio::writable_pipe wp{pool.get_executor()};
		boost::asio::connect_pipe(rp, wp);
		boost::system::error_code ec;
		boost::asio::write(wp, boost::asio::buffer("Hello, c++ world."), ec);
		// The write-pipe end must be closed after writing and before reading read-pipe end.
		wp.close();
		if (ec)
			throw std::system_error{ec, "error: write pipe"};
		std::string received;
		boost::asio::read(rp, boost::asio::dynamic_buffer(received), ec);
		if (ec && ec != boost::asio::error::eof)
			throw std::system_error{ec, "error: read pipe"};
		std::cout << "Received: " << received << std::endl;
	}
	catch (const std::system_error & e)
	{
		std::cerr
			<< "====>\n"
			<< "e.what(): " << e.what() << '\n'
			<< "e.code(): " << e.code() << '\n'
		;
	}
}

Build and Run

> b2
...found 10 targets...
> ./bin/gcc-15/debug/cxxstd-26-iso/bpp-1
Received: Hello, c++ world.

boost::process

boost::process v2 is default since boost 1.89.

boost::process::process proc{
	executor,
	"/path/prog",
	{param1, ...},
	boost::process::process_stdio{
		.in = ???,
		.out = ???,
		.err = ???
	}
};

boost::process c++ example 1

Filename: jamroot

project
:
	default-build
		<cxxstd>26
;

lib boost_process ;
lib boost_filesystem ;

exe bpp : bpp.cpp : <library>boost_process <library>boost_filesystem ;

Filename: bpp.cpp

#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <iostream>

namespace bpp = boost::process;
namespace asio = boost::asio;

// not hpp

int main()
{
	try
	{
		asio::thread_pool pool{32};
		asio::readable_pipe rp{pool.get_executor()};
		asio::writable_pipe wp{pool.get_executor()};
		asio::connect_pipe(rp, wp);
		bpp::process proc{
			pool.get_executor(),
			bpp::environment::find_executable("ugrep"),
			{
				"-rIni",
				R"(\.hpp)",
				"."
			},
			bpp::process_stdio{
				.out = wp
			}
		};
		if (proc.wait() != 0)
			throw std::runtime_error{"proc error"};
		wp.close();
		std::string result;
		bpp::error_code ec;
		asio::read(rp, asio::dynamic_buffer(result), ec);
		if (ec && ec != asio::error::eof)
			throw std::system_error{ec, "read pipe error"};
		std::cout << "Result:\n" << result << std::endl;
	}
	catch (const std::exception & e)
	{
		std::cout << "====>\n" << e.what() << std::endl;
	}
}

The example will run ugrep -rIni '\.hpp' .
and save result to c++ std::string result.

.out is assigned with a writable-pipe.

boost::process c++ example 2

Filename: bpp.cpp

#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <iostream>

namespace bpp = boost::process;
namespace asio = boost::asio;

// not hpp

int main()
{
	try
	{
		asio::thread_pool pool{32};
		asio::readable_pipe rp{pool.get_executor()};
		bpp::process proc{
			pool.get_executor(),
			bpp::environment::find_executable("ugrep"),
			{
				"-rIni",
				R"(\.hpp)",
				"."
			},
			bpp::process_stdio{
				.out = rp
			}
		};
		std::string result;
		boost::system::error_code ec;
		asio::read(rp, asio::dynamic_buffer(result), ec);
		if (ec && ec != asio::error::eof)
			throw std::system_error{ec, "error: read pipe"};
		std::cout << "Result:\n" << result << std::endl;
	}
	catch (const std::exception & e)
	{
		std::cout << "====>\n" << e.what() << std::endl;
	}
}

In this example, because .out is assigned with a readable-pipe,
a writable-pipe will be created internally and connected to the readable-pipe.

boost::process c++ example 3

Using boost::process in asynchrous and awaitable asio c++ code.

#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <iostream>

namespace app
{
	class program:
		virtual public std::enable_shared_from_this<app::program>
	{
	private:
		std::shared_ptr<boost::process::process> __proc;
		std::shared_ptr<boost::asio::readable_pipe> __rp;
	public:
		boost::asio::awaitable<void> start()
		{
			co_await this->init();
			co_await this->run_program();
			co_await this->get_output();
		}
	protected:
		boost::asio::awaitable<void> init()
		{
			__rp = std::make_shared<boost::asio::readable_pipe>(
				co_await boost::asio::this_coro::executor
			);
			__proc = std::shared_ptr<boost::process::process>{
				new boost::process::process{
					co_await boost::asio::this_coro::executor,
					boost::process::environment::find_executable("falkon"),
					std::initializer_list<boost::string_view>{
						"http://cppfx.i2p"
					},
					boost::process::process_stdio{
						.out = *__rp
					}
				}
			};
		}
		boost::asio::awaitable<void> run_program()
		{
			auto [ec, status] = co_await __proc->async_wait(
				boost::asio::as_tuple
			);
			boost::process::check_exit_code(ec, __proc->native_exit_code());
			if (ec || status != 0)
				throw std::system_error{ec, "wait proc error"};
			// else
			std::cout << "OK" << std::endl;
		}
		boost::asio::awaitable<void> get_output()
		{
			std::string result;
			auto [ec, bytes] = co_await boost::asio::async_read(
				*__rp,
				boost::asio::dynamic_buffer(result),
				boost::asio::as_tuple
			);
			if (ec && ec != boost::asio::error::eof)
				throw std::system_error{ec, "read pipe error"};
			if (result.empty())
				std::cout << "No data." << std::endl;
			else
				std::cout << "Result: " << result << std::endl;
		}
	};
}

int main()
{
	try
	{
		boost::asio::thread_pool pool{32};
		boost::asio::co_spawn(
			pool.get_executor(),
			std::bind(
				&app::program::start,
				std::make_shared<app::program>()
			),
			[] (std::exception_ptr eptr)
			{
				if (eptr)
					std::rethrow_exception(eptr);
			}
		);
		pool.join();
	}
	catch (const std::exception & e)
	{
		std::cout << "====>\n" << e.what() << std::endl;
	}
}

//////////////////////////////////////////////////////////////////////

Home

//////////////////////////////////////////////////////////////////////

Tue Dec 30 05:43:03 AM UTC 2025

//////////////////////////////////////////////////////////////////////

Role

Powered by - B2 Build | boost quickbook | I2Pd

====

cppfx.i2p

cppfxjjm5bgqx2cepvisfcy4zz4ystzxxh36mtuvqm2jp5g6rb7a.b32.i2p


PrevUpHomeNext