Boost C++ 库

……世界上最受推崇和设计精良的 C++ 库项目之一。 Herb SutterAndrei AlexandrescuC++ 编码规范

PrevUpHomeNext

第 29 章 Boost.Process

Klemens David Morgenstern

根据 Boost 软件许可证版本 1.0 分发。(参见附带文件 LICENSE_1_0.txt 或复制自 https://boost.ac.cn/LICENSE_1_0.txt)

目录

Process V1
简介
概念
教程
设计原理
扩展
常见问题
参考
Process V2
简介
快速入门
启动器
process_start_dir
标准输入输出
环境变量
参考
致谢
[Note] 注意

Process v1 将在下一个版本 (1.88) 中弃用。新项目请使用 v2。

Boost.Process 是一个用于管理系统进程的库。它可以用于

  • 创建子进程
  • 设置子进程的流
  • 通过流与子进程通信(同步或异步)
  • 等待进程退出(同步或异步)
  • 终止进程

以下是如何使用 Boost.Process 启动程序的简单示例

#include <boost/process.hpp>

#include <string>
#include <iostream>

using namespace boost::process;

int main()
{
    ipstream pipe_stream;
    child c("gcc --version", std_out > pipe_stream);

    std::string line;

    while (pipe_stream && std::getline(pipe_stream, line) && !line.empty())
        std::cerr << line << std::endl;

    c.wait();
}

本节将解释此库中使用的操作系统的一些底层概念。在接下来的章节中,我们将假设您已了解这些概念。但请注意,这只是一个简短的总结,并非涵盖所有内容。

此库的目标是实现一个可移植的包装器,因此我们将主要解释 Windows 和 POSIX 的共同之处。

管道是操作系统提供的用于不同线程、进程以及某些情况下机器之间通信的工具。

管道的典型特征是它是一个单通道,提供两个句柄,一个用于读取(源),一个用于写入(接收器)。这与其他工具(如套接字)不同,并提供另一种管理连接的方式:如果管道的一端关闭(即管道中断),则另一端会收到通知。

管道通常用于进程间通信。主要原因是管道可以直接分配给进程的标准输入输出,即 stderr、stdin 和 stdout。此外,管道的一半可以继承到子进程,并在父进程中关闭。这将导致子进程退出时管道中断。

但请注意,如果同一线程读取和写入管道,它只会与自身通信。

最常见的管道是匿名管道。由于它们没有名称,因此只能通过复制任一句柄来获取它们的句柄。

在此库中,以下函数用于创建无名管道

顾名思义,命名管道具有字符串标识符。这意味着也可以使用标识符获取它们的句柄。

POSIX 实现使用 FIFO,这意味着命名管道表现得像文件。

Windows 提供了一个名为 命名管道 的工具,它也具有类似文件的名称,但与实际文件系统处于不同的范围。

[Note] 注意

命名管道成为此库一部分的主要原因是它们需要在 Windows 上用于异步通信。

进程是一个独立的可执行实体,它与线程不同,因为它拥有自己的资源。这些资源包括内存和硬件资源。

每个进程都由一个唯一的数字[27]标识,称为进程标识符,pid

进程将返回一个整数值,指示其是否成功。在 POSIX 中,与此相关的代码更多,但在 Windows 中则不是这样。因此,库中目前没有此类编码。但是,退出代码为零表示进程成功,而不同于零的值表示错误。

也可以强制进程退出。有两种方法可以做到这一点:发出信号让进程退出并等待,以及直接终止进程而无需任何条件。

通常,第一种方法是发出退出请求,但 Windows 与 POSIX 不同,它没有提供一致的方法来执行此操作。因此,这并非库的一部分,只有强制终止是。

环境是每个进程的局部变量映射。对于此库而言,最重要的变量是 PATH 变量,它包含应搜索可执行文件的路径列表。shell 会自动执行此操作,而此库为此提供了一个函数。

在本节中,我们将逐步介绍 boost.process 的不同功能。有关完整说明,请参阅 参考概念 部分。

我们想启动一个进程,所以让我们从一个简单的进程开始。我们将调用 gcc 编译器来编译一个简单的程序。

使用标准库,它看起来像这样。

int result = std::system("g++ main.cpp");

我们可以在 boost.process 中这样编写。

namespace bp = boost::process; //we will assume this for all further examples
int result = bp::system("g++ main.cpp");

如果给定单个字符串(或显式形式 bp::cmd),它将被解释为命令行。这将导致执行函数搜索 PATH 变量以查找可执行文件。另一种方法是 exe-args 样式,其中第一个字符串将被解释为文件名(包括路径),其余部分将作为传递给该函数的参数。

[Note] 注意

有关 cmd/exe-args 样式的更多详细信息,请参阅 此处

因此,第一步,我们将使用 exe-args 样式。

int result = bp::system("/usr/bin/g++", "main.cpp");

使用该语法,我们仍然将“g++”硬编码,因此让我们假设我们从外部源获取字符串作为 boost::process::v1::filesystem::path,我们也可以这样做。

boost::process::v1::filesystem::path p = "/usr/bin/g++"; //or get it from somewhere else.
int result = bp::system(p, "main.cpp");

现在我们可能想要在 PATH 变量中查找 g++ 可执行文件,就像 cmd 语法那样。Boost.process 提供了一个为此目的的函数:bp::search_path

boost::process::v1::filesystem::path p = bp::search_path("g++"); //or get it from somewhere else.
int result = bp::system(p, "main.cpp");

[Note] 注意

search_path 将搜索具有该名称的任何可执行文件。这也包括在 Windows 上添加文件后缀,例如 .exe.bat

鉴于我们的示例使用了 system 函数,我们的程序将等到子进程完成。这可能是我们不希望的,特别是编译可能需要一段时间。

为了避免这种情况,boost.process 提供了几种启动进程的方法。除了前面提到的 system 函数及其异步版本 async_system 之外,我们还可以使用 spawn 函数或 child 类。

spawn 函数启动一个进程并立即分离它,因此不会返回句柄,并且会忽略该进程。这不是我们编译所需的,但也许我们想在编译时娱乐用户

bp::spawn(bp::search_path("chrome"), "www.boost.org");

现在让我们来看一下更合理的编译方法:非阻塞执行。为此,我们直接调用 child 的构造函数。

bp::child c(bp::search_path("g++"), "main.cpp");

while (c.running())
    do_some_stuff();

c.wait(); //wait for the process to exit   
int result = c.exit_code();

我们通过调用子构造函数来启动进程。然后,在进程运行期间和运行之后,我们检查并执行其他操作,并获取退出代码。调用wait 是必要的,以便获取退出代码并告诉操作系统,没有人再等待该进程了。

[Note] 注意

您还可以使用wait_forwait_until 来等待一段时间或直到某个时间点。

[Warning] 警告

如果您不调用子对象上的 wait,则它会在销毁时被终止。这可以通过事先调用detach 来避免。

到目前为止,我们假设一切都能正常运行,但“g++”可能不存在,这将导致进程启动失败。所有函数的默认行为是在失败时抛出std::system_error。与该库中的许多其他函数一样,传递std::error_code 将改变行为,这样就不会抛出异常,而是将错误赋值给错误代码。

std::error_code ec;
bp::system("g++ main.cpp", ec);

在上面给出的示例中,我们只启动了一个程序,但没有考虑输出。默认值取决于系统,但通常情况下,这只会将输出写入与启动进程相同的输出。如果需要保证这一点,可以像这样显式地转发流。

bp::system("g++ main.cpp", bp::std_out > stdout, bp::std_err > stderr, bp::std_in < stdin);

现在对于第一个示例,我们可能只想忽略输出,这可以通过将其重定向到空设备来实现。可以这样实现:

bp::system("g++ main.cpp", bp::std_out > bp::null);

或者,我们也可以轻松地将输出重定向到文件。

bp::system("g++ main.cpp", bp::std_out > "gcc_out.log");

现在,让我们来看一个更直观的读取数据的示例。nm 是 posix 上的一个工具,它读取二进制文件的概要,即所有入口点的列表。每个入口点都将放在一行中,我们将使用管道来读取它。最后附加一个空行,我们用它作为停止读取的指示。Boost.process 提供了 pipestream(ipstreamopstreampstream)来包装pipe 并提供std::istreamstd::ostreamstd::iostream 接口的实现。

std::vector<std::string> read_outline(std::string & file)
{
    bp::ipstream is; //reading pipe-stream
    bp::child c(bp::search_path("nm"), file, bp::std_out > is);

    std::vector<std::string> data;
    std::string line;

    while (c.running() && std::getline(is, line) && !line.empty())
        data.push_back(line);

    c.wait();

    return data;
}

这样做会将进程的stdout 重定向到一个管道中,我们同步地读取它。

[Note] 注意

您可以对std_err 执行相同的操作。

现在我们从nm 获取名称,我们可能想要对其进行反混淆,因此我们使用输入和输出。nm 有一个反混淆选项,但为了举例说明,我们将为此使用c++filt

bp::opstream in;
bp::ipstream out;

bp::child c("c++filt", std_out > out, std_in < in);

in << "_ZN5boost7process8tutorialE" << endl;
std::string value;
out >> value;

c.terminate();

现在您可能想要将一个进程的输出转发到另一个进程的输入。

std::vector<std::string> read_demangled_outline(const std::string & file)
{
    bp::pipe p;
    bp::ipstream is;

    std::vector<std::string> outline;

    //we just use the same pipe, so the output of nm is directly passed as input to c++filt
    bp::child nm(bp::search_path("nm"), file,  bp::std_out > p);
    bp::child filt(bp::search_path("c++filt"), bp::std_in < p, bp::std_out > is);

    std::string line;
    while (filt.running() && std::getline(is, line)) //when nm finished the pipe closes and c++filt exits
        outline.push_back(line);

    nm.wait();
    filt.wait();
}

这将nm 的数据转发到c++filt,而无需您的进程执行任何操作。

Boost.process 允许使用 boost.asio 来实现异步 I/O。如果您熟悉boost.asio(我们强烈推荐),您可以使用async_pipe,它作为 I/O 对象实现,可以像上面显示的pipe 一样使用。

现在我们回到我们的编译示例。对于nm,我们可能会逐行分析输出,但编译器输出将只放入一个大型缓冲区中。

使用boost.asio,它看起来是这样的。

boost::asio::io_service ios;
std::vector<char> buf(4096);

bp::async_pipe ap(ios);

bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > ap);

boost::asio::async_read(ap, boost::asio::buffer(buf),
                [](const boost::system::error_code &ec, std::size_t size){});

ios.run();
int result = c.exit_code();

为了简化操作,Boost.process 提供了一个更简单的接口,这样就可以直接传递缓冲区,前提是我们还传递对boost::asio::io_service 的引用。

boost::asio::io_service ios;
std::vector<char> buf(4096);

bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > boost::asio::buffer(buf), ios);

ios.run();
int result = c.exit_code();

[Note] 注意

boost::asio::io_service 的实例传递给启动函数会自动使其异步等待退出,因此无需调用wait

为了更方便,您可以使用std::future 进行异步操作(您仍然需要将对boost::asio::io_service 的引用传递给启动函数),除非您使用bp::systembp::async_system

现在我们将重新审视我们的第一个示例,并异步读取编译器输出。

boost::asio::boost::asio::io_service ios;

std::future<std::string> data;

child c("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);


ios.run(); //this will actually block until the compiler is finished

auto err =  data.get();

启动多个进程时,可以将它们组合在一起。如果子进程不修改组成员身份,这也适用于启动其他进程的子进程。例如,如果您调用make(它启动其他进程)并在其上调用 terminate,除非您使用组,否则它不会终止所有子进程。

使用组的两个主要原因是:

  1. 能够终止子进程的子进程。
  2. 将多个进程组合成一个,以便可以一次终止它们。

如果我们有一个像make 这样的程序,它会启动它自己的子进程,调用terminate 可能不够。例如,如果我们有一个启动gcc 的 makefile 并使用以下代码,gcc 进程仍将在之后运行。

bp::child c("make");
if (!c.wait_for(std::chrono::seconds(10))) //give it 10 seconds
    c.terminate(); //then terminate

因此,为了终止gcc,我们可以使用组。

bp::group g;
bp::child c("make", g);
if (!g.wait_for(std::chrono::seconds(10)))
    g.terminate();

c.wait(); //to avoid a zombie process & get the exit code

在给定的示例中,我们仍然调用wait 以避免僵尸进程。一个更简单的解决方案可能是使用spawn

要将两个进程放入一个组中,以下代码就足够了。Spawn 已经启动了一个分离的进程(即没有子句柄),但它们可以分组,以便在出现问题时,RAII 仍然是给定的。

void f()
{
    bp::group g;
    bp::spawn("foo", g);
    bp::spawn("bar", g);

    do_something();

    g.wait();
};

在示例中,它将在函数结束时等待这两个进程,除非发生异常。即,如果抛出异常,则该组将被终止。

有关更多信息,请参阅boost/process/group.hpp

此库提供对当前进程环境的访问,并允许为子进程设置环境。

//get a handle to the current environment
auto env = boost::this_process::environment();
//add a variable to the current environment
env["VALUE_1"] = "foo";

//copy it into an environment separate to the one of this process
bp::environment env_ = env;
//append two values to a variable in the new env
env_["VALUE_2"] += {"bar1", "bar2"};

//launch a process with `env_`
bp::system("stuff", env_);

修改子进程环境的一种更方便的方法是env 属性,它可以在示例中按如下方式使用:

bp::system("stuff", bp::env["VALUE_1"]="foo", bp::env["VALUE_2"]+={"bar1", "bar2"});

有关更多信息,请参阅boost/process/environment.hpp

此库旨在为不同的操作系统特定方法提供一个包装器来启动进程。其目标是提供在这些系统上可用的所有功能,并允许用户执行所有需要使用操作系统 API 的相关操作。

此库不尝试为所有与进程相关的操作提供一个完整的库。 在许多讨论中,有人提议将 boost.process 构建成某种形式的 DSEL[28]。这不是目标,它更侧重于提供在它之上构建此类 DSEL 库的工具。因此,该库也不会强迫用户使用任何特定的方式(例如仅异步通信)。它可以与这样的库集成。

Boost.Process 在构建进程时使用了非常特殊的风格。这是因为进程包含许多属性,而这些属性并非实际子类的成员。在许多情况下,父进程无法访问这些属性,例如使用环境变量时。子进程可以修改自己的环境,但父进程无法知道这一点。这意味着子进程具有一些在 C++ 中无法访问的属性。

这就导致了该库支持和混合使用的两种风格:重载和属性。假设您可能想要启动一个进程并传递多个参数。这两种风格都支持这种方式,如下所示

system("gcc", "--version"); //overloading
system("gcc", args={"--version"}); //property style.

在某些情况下,这两种风格也可以混合使用。

system("gcc", "-c", args+={"main.cpp"});

在下一节中,将描述可用的风格。请注意,重载风格是通过类型特征实现的,因此将列出这些类型。

[Caution] 警告

无法保证参数的应用顺序!但是,可以保证属于同一组的参数的顺序,即字符串参数和 args 属性将按给定的顺序进行评估。

向进程传递参数时,提供两种风格:cmd 风格和 exe-/args 风格。

cmd 风格将字符串解释为 exe 和参数的序列并按此进行解析,而 exe-/args 风格将每个字符串解释为一个参数。

表 29.1. Cmd 与 Exe/Args

字符串

Cmd

Exe/Args

"gcc --version"

{"gcc", "--version"}

{"\"gcc --version\""}


使用重载变体时,单个字符串将导致 cmd 解释,多个字符串将产生 exe-args 解释。两种版本都可以显式设置。

system("grep -c false /etc/passwd"); //cmd style
system("grep", "-c", "false", "/etc/passwd"); //exe-/args-

system(cmd="grep -c false /etc/passwd"); //cmd style
system(exe="grep", args={"-c", "false", "/etc/passwd"}); //exe-/args-

[Note] 注意

如果在参数风格中使用 '"' 符号,它将作为参数的一部分传递。如果希望 cmd 语法产生相同的效果,则应进行转义,即 '\"'。

[Note] 注意

在命令风格中,将自动搜索PATH变量,但搜索的是启动进程的变量,而不是传递给子进程的变量。

扩展功能的最简单形式是提供另一个处理程序,该处理程序将在进程启动时的相应事件上被调用。名称如下:

  • boost::process::v1::on_setup
  • boost::process::v1::on_error
  • boost::process::v1::on_success

例如

child c("ls", on_setup([](){cout << "On Setup" << endl;}));

[Note] 注意

在 POSIX 系统上,所有这些回调都将由该进程处理,而不是由创建的进程处理。这与 POSIX 扩展不同,POSIX 扩展可以在派生进程上执行。

要扩展库,请使用boost/process/extend.hpp头文件。

它只提供自定义属性的显式风格,而不提供隐式风格。

这意味着可以实现自定义初始化程序,一个可以传递给启动函数之一的引用。如果一个类继承了boost::process::v1::extend::handler,它将被视为初始化程序,并直接放入执行器接收的序列中。

执行器在进程启动期间调用初始化程序的不同处理程序。基本结构由三个函数组成,如下所示

此外,POSIX 还提供另外三个处理程序,如下所示

有关更多信息,请参阅posix_executor的参考文档。

最简单的扩展只接收单个处理程序,这可以用函数式风格来实现。因此,让我们从一个简单的 hello-world 示例开始,同时使用 C++14 通用 lambda 表达式。

using namespace boost::process;
namespace ex = bp::extend;

child c("foo", ex::on_success=[](auto & exec) {std::cout << "hello world" << std::endl;});

考虑到 lambda 表达式也可以捕获值,数据可以很容易地在处理程序之间共享。

要查看执行器有哪些成员,请参考windows_executorposix_executor

[Note] 注意

结合on_exit,这也可以处理进程退出。

[Caution] 警告

POSIX 处理程序符号未在 Windows 上定义。

由于前面的示例采用函数式风格,因此它不太可重用。为了解决这个问题,handlerboost::process::v1::extend命名空间中有一个别名,可以继承它。因此,让我们用类来实现 hello world 示例。

struct hello_world : handler
{
    template<typename Executor>
    void ex::on_success(Executor & exec) const
    {
        std::cout << "hello world" << std::endl;
    }
};

//in our function
child c("foo", hello_world());

[Note] 注意

实现是通过重载而不是重写来完成的。

每个未实现的处理程序都默认为handler,其中为每个事件定义了一个空处理程序。

由于boost.process提供了对boost.asio的接口,因此扩展也可用此功能。如果类需要boost::asio::io_context,则可以使用以下代码。

struct async_foo : handler, ex::require_io_context
{
    template<typename Executor>
    void on_setup(Executor & exec)
    {
        boost::asio::io_context & ios = ex::get_io_context(exec.seq); //gives us a reference and a compiler error if not present.
        //do something with ios
    }
};

[Note] 注意

继承require_io_context是必要的,因此system提供了一个。

此外,处理程序可以提供一个在子进程退出时调用的函数。这是通过ex::async_handler来完成的。

[Note] 注意

async_handler隐含require_io_context

struct async_bar : __handler, ex::async_handler
{
    template<typename Executor>
    std::function<void(int, const std::error_code&)> on_exit_handler(Executor & exec)
    {
        auto handler_ = this->handler;
        return [handler_](int exit_code, const std::error_code & ec)
               {
                   std::cout << "hello world, I exited with " << exit_code << std::endl;
               };

    }
};

[Caution] 警告

on_exit_handler不是默认的,并且在继承async_handler时始终是必需的。

[Caution] 警告

on_exit_handler使用boost::asio::signal_set在 POSIX 系统上监听 SIGCHLD 信号。应用程序也不应使用signal()sigaction()等函数注册 SIGCHLD 信号处理程序(但可以使用boost::asio::signal_set)。

如果初始化程序中发生错误,则应将其告知执行器,而不是直接处理。这是因为可以通过传递给启动函数的参数来更改行为。因此,执行器具有set_error函数,该函数接受一个std::error_code和一个字符串。根据执行器的配置,这可能会抛出异常,设置内部error_code,或者什么也不做。

因此,让我们来看一个简单的示例,我们设置一个随机选择的error_code

auto set_error = [](auto & exec)
        {
            std::error_code ec{42, std::system_category()};
            exec.set_error(ec, "a fake error");

        };
child c("foo", on_setup=set_error);

由于我们在此示例中未指定错误处理模式,因此这将抛出process_error

现在我们有了自定义初始化程序,让我们考虑如何处理不同执行器之间的差异。区别在于 POSIX 和 Windows,以及 Windows 上的charwchar_t。一种解决方案是使用BOOST_WINDOWS_API 和 BOOST_POSIX_API宏,这些宏在包含任何 process 头文件后都会自动可用。

另一个变体是类型别名ex::posix_executorex::windows_executor,其中当前系统以外的执行器是前向声明。这可以正常工作,因为该函数永远不会被调用。因此,让我们实现另一个示例,它打印可执行文件名ex::on_success

struct hello_exe : handler
{
    template<typename Sequence>
    void ex::on_success(ex::posix_executor<Sequence> & exec)
    {
        std::cout << "posix-exe: " << exec.exe << std::endl;
    }

    template<typename Sequence>
    void ex::on_success(ex::windows_executor<char, Sequence> & exec)
    {
        //note: exe might be a nullptr on windows.
        if (exec.exe != nullptr)
            std::cout << "windows-exe: " << exec.exe << std::endl;
        else
            std::cout << "windows didn't use exe" << std::endl;
    }

    template<typename Sequence>
    void ex::on_success(ex::windows_executor<wchar_t, Sequence> & exec)
    {
        //note: exe might be a nullptr on windows.
        if (exec.exe != nullptr)
            std::wcout << L"windows-exe: " << exec.exe << std::endl;
        else
            std::cout << "windows didn't use exe" << std::endl;
    }

};

因此,根据我们的示例,使用非本机执行器的定义仍然是一个模板,因此如果未使用,则不会对其进行评估。因此,这提供了一种在不使用预处理器的情况下实现系统特定代码的方法。

[Note] 注意

如果您只编写部分实现,例如只针对ex::posix_executor,其他变体将默认为handler

.

现在让我们重新审视我们的c++filt示例,我们将引入一个明显的错误。然而,对于更复杂的应用程序,这可能并不那么明显。

vector<string> demangle(vector<string> in)
{

    ipstream is;
    opstream os;
    child c("c++filt", std_out > is, std_in < os);

    vector<string> data;
    for (auto & elem : data)
    {
        string line;
        getline(is, line);
        os << elem;
    }
}

我们颠倒了读写操作,导致了死锁。这会立即锁定。这是因为c++filt期望在输出任何数据之前输入数据。另一方面,启动进程等待其输出。

再举一个例子,这个例子看起来可能是正确的,假设您想使用ls读取当前目录。

ipstream is;
child c("ls", std_out > is);

std::string file;
while (is >> file)
    cout << "File: " << file << endl;

这也会导致死锁,因为子进程退出时管道不会关闭。ipstream即使进程已结束,仍然会查找数据。

[Note] 注意

在这个库中无法使用自动管道关闭,因为管道可能是一个文件句柄(例如 Windows 上的异步管道)。

但是,由于管道是缓冲的,如果您这样做,可能会得到不完整的数据。

ipstream is;
child c("ls", std_out > is);

std::string file;
while (c.running())
{
    is >> file;
    cout << "File: " << file << endl;
}

因此,如果您不确定输出是什么样的,强烈建议您使用异步 API。

由于 Windows 不使用 UTF-8,因此有时不可避免地要使用 WinApi 的wchar_t版本。为了保持此库的一致性,它还在 posix 上提供了wchar_t支持。

由于 posix api 纯粹是char,所以每个基于wchar_t的类型都将转换为char

另一方面,Windows 更具选择性;默认情况下使用char,但如果任何参数需要wchar_t,则所有内容都将转换为wchar_t。这也包括boost::filesystem::path。此外,如果系统不提供char api(如 Windows CE 的情况),所有内容也将被转换。

参考

namespace boost {
  namespace process {
    namespace v1 {
      class async_pipe;
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      template<typename ExitHandler, typename ... Args> 
        unspecified async_system(boost::asio::io_context &, ExitHandler &&, 
                                 Args &&...);
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      class child;

      typedef unspecified pid_t;  // Typedef for the type of an pid_t. 
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      template<typename Char> class basic_environment;
      template<typename Char> class basic_native_environment;

      typedef basic_native_environment< char > native_environment;  // Definition of the environment for the current process. 
      typedef basic_native_environment< wchar_t > wnative_environment;  // Definition of the environment for the current process. 
      typedef basic_environment< char > environment;  // Type definition to hold a seperate environment. 
      typedef basic_environment< wchar_t > wenvironment;  // Type definition to hold a seperate environment. 
    }
  }
  namespace this_process {

    // Get the process id of the current process. 
    int get_id();

    // Get the native handle of the current process. 
    native_handle_type native_handle();

    // Get the enviroment of the current process. 
    native_environment environment();

    // Get the enviroment of the current process. 
    wnative_environment wenvironment();

    // Get the path environment variable of the current process runs. 
    std::vector< boost::process::v1::filesystem::path > path();
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      struct process_error;
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      namespace extend {
        struct async_handler;
        struct handler;
        template<typename Sequence> struct posix_executor;
        struct require_io_context;
        template<typename Char, typename Sequence> struct windows_executor;

        unspecified on_setup;        // This handler is invoked before the process in launched, to setup parameters. The required signature is void(Exec &), where Exec is a template parameter. 
        unspecified on_error;        // This handler is invoked if an error occurred. The required signature is void(auto & exec, const std::error_code&), where Exec is a template parameter. 
        unspecified on_success;        // This handler is invoked if launching the process has succeeded. The required signature is void(auto & exec), where Exec is a template parameter. 
        unspecified on_fork_error;        // This handler is invoked if the fork failed. The required signature is void(auto & exec), where Exec is a template parameter. 
        unspecified on_exec_setup;        // This handler is invoked if the fork succeeded. The required signature is void(Exec &), where Exec is a template parameter. 
        unspecified on_exec_error;        // This handler is invoked if the exec call errored. The required signature is void(auto & exec), where Exec is a template parameter. 

        // Helper function to get the last error code system-independent. 
        std::error_code get_last_error();
        void throw_last_error(const std::string &);
        void throw_last_error();
        template<typename Sequence> 
          asio::io_context & get_io_context(const Sequence &);
      }
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      class group;
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      static unspecified limit_handles;
    }
  }
  namespace this_process {
    typedef unspecified native_handle_type;
    std::vector< native_handle_type > get_handles();
    std::vector< native_handle_type > get_handles(std::error_code & ec);
    bool is_stream_handle(native_handle_type);
    bool is_stream_handle(native_handle_type handle, std::error_code & ec);
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      typedef std::codecvt< wchar_t, char, std::mbstate_t > codecvt_type;  // The internally used type for code conversion. 

      // Internally used error cateory for code conversion. 
      const std::error_category & codecvt_category();

      // Get a reference to the currently used code converter. 
      const codecvt_type & codecvt();

      // Set the locale of the library. 
      std::locale imbue(const std::locale & loc);
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      template<typename CharT, typename Traits = std::char_traits<CharT> > 
        class basic_ipstream;
      template<typename CharT, typename Traits = std::char_traits<CharT> > 
        class basic_opstream;
      template<typename CharT, typename Traits = std::char_traits<CharT> > 
        class basic_pipe;

      template<typename CharT, typename Traits = std::char_traits<CharT> > 
        struct basic_pipebuf;

      template<typename CharT, typename Traits = std::char_traits<CharT> > 
        class basic_pstream;

      typedef basic_pipe< char > pipe;
      typedef basic_pipe< wchar_t > wpipe;
      typedef basic_pipebuf< char > pipebuf;
      typedef basic_pipebuf< wchar_t > wpipebuf;
      typedef basic_ipstream< char > ipstream;
      typedef basic_ipstream< wchar_t > wipstream;
      typedef basic_opstream< char > opstream;
      typedef basic_opstream< wchar_t > wopstream;
      typedef basic_pstream< char > pstream;
      typedef basic_pstream< wchar_t > wpstream;
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      boost::process::v1::filesystem::path 
      search_path(const boost::process::v1::filesystem::path &, 
                  const std::vector< boost::process::v1::filesystem::path > = ::boost::this_process::path());
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      template<typename ... Args> void spawn(Args &&...);
    }
  }
}
namespace boost {
  namespace process {
    namespace v1 {
      template<typename ... Args> int system(Args &&...);
    }
  }
}


[27] 只要进程处于活动状态,它就是唯一的

[28] 领域特定嵌入式语言


PrevUpHomeNext