Where is Boost.Process?
Julio M. Merino Vidal, who is, I beleive, the original author, wrote in this 2007 post that he did not have time to complete it.
Development was taken over by Boris Schaeling. This is the version that you found at http://www.highscore.de/boost/process/. According to this post, he is still actively developing it.
There is another version, by Ilya Sokolov.
For your other question:
Could you perhaps suggest other cross-platform libraries for managing simple starting of and interation with external processes?
you could look at this wiki page listing alternatives.
Depending on your needs, popen() could also do the job.
Boost process open process in new window (Windows)
ITNOA
As you can see in Boost::process hide console on windows, you can create new mode for creating process.
CreateProcessA function system call, Show yourself to how to create new process with new console, by helping creation flags: CREATE_NEW_CONSOLE
(thanks to Using multiple console windows for output)
you can write code like below
struct new_window
: ::boost::process::detail::handler_base
{
// this function will be invoked at child process constructor before spawning process
template <class WindowsExecutor>
void on_setup(WindowsExecutor &e) const
{
e.creation_flags = ::boost::detail::winapi::CREATE_NEW_CONSOLE_;
}
};
and for using this, you can just write something like below
::boost::process::child ch("./worker.exe", new_window);
Boost Process 1.71 does not look in environment path anymore
I haven't checked whether the behaviour actually changed, but I know there's a way to explicitly allow Boost to search the path:
Keep in mind that searching the path can easily be a security issue because it can be compromised, or attackers can leverage knowledge of the path setting to intercept executables. This is why you'd expect
search_path
to be OFF by default (except for thesystem
interface, which is traditionally insecure)
Live On Wandbox
#include <boost/process.hpp>
int main() {
namespace bp = boost::process;
bp::child c(
bp::search_path("date"),
std::vector<std::string> { "+%s" });
c.wait();
}
Is there a way to log the output of a process create via boost::process::spawn?
No that is not possible. It's implied in the documentation:
This function does not allow asynchronous operations, since it cannot
wait for the end of the process. It will fail to compile if a
reference to boost::asio::io_context is passed.
Maybe you can use child::detach
instead?
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <iostream>
namespace bp = boost::process;
int main()
{
boost::asio::io_service ios;
std::future<std::string> data;
bp::child c("/usr/bin/g++", "main.cpp", // set the input
bp::std_in.close(),
bp::std_out > bp::null, // so it can be written without anything
bp::std_err > data, ios);
c.detach();
ios.run(); // this will actually block until the compiler is finished
auto err = data.get();
std::cout << err;
}
Set the working directory when starting a process with boost
#include <boost/process/start_dir.hpp>
namespace bp = boost::process;
int result = bp::system("/usr/bin/g++", "main.cpp", bp::start_dir("/home/user"));
bp::child c(bp::search_path("g++"), "main.cpp", bp::start_dir("/home/user"));
c.wait();
See boost::process::start_dir and the complete Reference.
Args are program name, program args and other process properties from the Reference.
Cannot send and execute correct command through pipes using Boost library in C++
You are printing to cout
as if the async operations happen immediately. That's not the case. The async operations only happen when the io service runs.
svc.run();
Is at the very end of your code. So no async_
operation ever completes (or even starts) before that.
Other problems:
Your
out
async pipe is never used (not even connected). It's unclear to me how you intend to communicate with the child process that way.In fairness, you only every write to the child process, so maybe you're not at all interested in the output. (But then perhaps
recv_buffer
can be deleted just as well).Your buffers include the terminating
NUL
characters. (asio::buffer("uci\n")
sends{'u','c','i','\n','\0'}
). That's going to mess up the child processes's parsing.You do
in.close()
in response to every singleasync_write
completion. This guarantees that subsequent writes never can happen, as you closed the pipe.Then when you send
quit
you fail to include the '\n' as wellYou are reading into a
char[64]
withoperator>>
which makes no sense at all. Maybe you are using c++20 (so width of 64 might be assumed) but you never set a width. Most likely you would want to read into a string instead.However, doing so cannot accept commands with whitespace (because
std::ios::skipws
is set by default). So, likely you wantedstd::getline
instead...The fact that you include a boatload of C headers makes me think you're porting some C code (badly). That's also exemplified by the
strcmp
use and others, e.g. no need to use::stat
Don't use
using namespace std;
(Why is "using namespace std;" considered bad practice?)Don't use global variables (
errDetails
)Don't use loops to wait for a time delay
No need to manually print backtraces. Instead, use Boost:
void abort_application(std::string const& errDetails) {
std::cerr << errDetails << "\n";
std::cerr << boost::stacktrace::stacktrace{} << std::endl;
std::this_thread::sleep_for(3s);
abort();
}
Existing Stockfish Client: Playing Games
You're in luck: I have a written full demo using stockfish on this site: Interfacing with executable using boost in c++.
This example shows how to correctly await and parse expected replies from the child process(es).
You will note that I chose coroutines for the async version:
Just for completeness, I thought I'd try an asynchronous implementation. Using the default Asio callback style this could become unwieldy, so I thought to use Boost Coroutine for the stackful coroutines. That makes it so the implementation can be 99% similar to the synchronous version
Just for comparison, here's what your code should look like if you didn't use coroutines:
Fixing Up Your Code
Live On Coliru
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/stacktrace/stacktrace.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>
namespace bp = boost::process;
using boost::system::error_code;
using namespace std::literals;
static void abort_application(std::string const& errDetails) {
std::cerr << errDetails << "\n";
std::cerr << boost::stacktrace::stacktrace{} << std::endl;
std::this_thread::sleep_for(3s);
abort();
}
inline static bool stockfish_check_exists(std::string& name) {
return boost::filesystem::exists(name);
}
int main() {
boost::asio::io_service svc;
bp::async_pipe in{svc};
std::string proc = "/usr/games/stockfish";
if (!stockfish_check_exists(proc)) {
abort_application("Stockfish not found!");
}
auto on_exit = [](int code, std::error_code ec) {
std::cout << "Exited " << code << "(" << ec.message() << ")\n";
};
bp::child process(proc, bp::std_in < in, svc, bp::on_exit = on_exit);
std::function<void()> command_loop;
std::string command_buffer;
command_loop = [&] {
std::cout << "Enter your command: " << std::flush;
// boost::asio::streambuf recv_buffer;
if (getline(std::cin, command_buffer)) {
std::cout << "Your command is: " << command_buffer << std::endl;
command_buffer += '\n';
async_write( //
in, boost::asio::buffer(command_buffer),
[&](error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << " (" << ec.message() << ")" << std::endl;
if (command_buffer == "quit\n") {
std::cout << "Quiting......." << std::endl;
// in.close();
std::cout << "Engine quit!" << std::endl;
} else {
command_loop(); // loop
}
});
}
};
std::cout << "uci send" << std::endl;
async_write(
in, boost::asio::buffer("uci\n"sv),
[&](error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
std::cout << "isready send" << std::endl;
async_write(in, boost::asio::buffer("isready\n"sv),
[&](error_code ec, size_t n) {
std::cout << "Write: " << n << std::endl;
command_loop(); // start command loop
});
});
svc.run(); // only here any of the operations start
}
Prints, e.g.
Or if Stockfish is in fact installed:
Related Topics
Statically Linking System Libraries, Libc, Pthreads, to Aid in Debugging
Pthread Condition Variables Not Signalling Even Though Set to Pthread_Process_Shared
How to Enable C++11 in Qt Creator
Windows 7 Timing Functions - How to Use Getsystemtimeadjustment Correctly
Correct Use of Std::Cout.Precision() - Not Printing Trailing Zeros
Checking for Null Pointer in C/C++
Difference Between Char* and Char[]
C++11: Correct Std::Array Initialization
May I Take the Address of the One-Past-The-End Element of an Array
Why Isn't Malloc Filling Up Memory
Single Process Maximum Possible Memory in X64 Linux
Regex Replace with Callback in C++11
How to Implement Multithread Safe Singleton in C++11 Without Using <Mutex>