最近想做一个进程管理器,因为这段时间在学boost,所以就选择了boost::process,但一用才发现,VC2010下编译报错了。
原因是process中用到了不少C++11的语法,2010支持不完整。
所以,就有了这次的移植笔记。
不用告诉我VC2019可以完美支持,本人就是觉得VC2010够清新简洁,不会启动那么多乱七八糟的进程,浪费资源,哈哈哈。
其实重复造轮子才能真正学到别人代码的设计理念嘛,估计这个才算是瞎折腾的真正动力。
移植后的代码丢到github上了,地址在最后可以找到。
process移植笔记
元函数
元函数
是在boost::mpl
的文档中看到的,感觉挺有意思,而且也被process大量使用,因此,有必要先做个铺垫,这样后面的内容才好展开。
在理解元函数之前,先看看C++中的一个常规函数长啥样,两者结合,可能会更容易理解元函数的概念。
常规函数的基本要素
int MyFunc(int fld1)
{
//根据实际需要,干点有意义的事情
return 111;
}
上面定义了一个函数,名字为MyFunc,其包含3个重要部分:
- 入参
- 函数体
- 返回结果
MyFunc的作用是:将输入的入参fld1,经过一定的处理后,得到我们想要的结果111。
元函数使用场景举例
为了对元函数有个初步印象,我们先来看一个实际的使用场景。
试想有个通讯模块,负责将本机的数据发给远程主机,这时,我们要考虑以下这些问题:
- 对于整数类型,不同机器的字节序不一样,有的是小端,有的是大端,传输时,统一用大端
- 字符串,我们希望增加一个长度指示符,用于接收方计算字符串的实际长度
- 浮点数,不同机器的表示格式不尽相同,我们将其转换为整数后,再发送
按面向对象的设计思想,我们自然会想到,将不同的类型,封装成不同的类。例如,CInt负责整数的大小端转换,CStr负责字符串的处理,CFloat负责将浮点数转换为64位的整数值。
接下来,我们还需要定义一个Send函数,负责数据的发送:
template<typename T>
int Send(const T &t)
{
MyFunc<T> s(t); //注意,这里的代码是不合法的,C++不支持这种语法,编译会报错
//将s转换后的内容,发送出去
}
上面代码想实现的目标是:
- 入参T对应本机的实际类型int、char *、float等。
- Send中,先将
t
转换为对应的CInt、CStr、CFloat的实例,进行数据转换,然后,将转换后的数据,发送出去。
这里的关键点在于对s的类型声明,如果有一个类似MyFunc的“函数”,可以将输入类型T,转换为我们想要的CInt、CStr等类型的话,上面的代码就能通过编译了。
元函数定义
C++中并没有元函数的声明语法,在mpl中,是借用模板来实现的。
template<typename fld1> //元函数入参
struct MyFunc
{
typedef ...... var1; //可使用typedef定义一些变量
//std和mpl中,提供了一些标准模板,可实现类似条件判断、循环等操作
typedef ...... type; //这个type就是元函数的返回结果,算是mpl中约定俗成的做法
};
在MyFunc这个模板类的“{}”内部,相当于是元函数的函数体,模板参数就是元函数的入参,type为出参。
而我们前面写的Send函数,就变成下面这个样子:
template<typename T>
int Send(const T &t)
{
MyFunc<T>::type s(t); //区别就在这里
//将s转换后的内容,发送出去
}
在真实世界中,元函数的实现往往不止一个模板定义,而是使用了模板部分特化技术,分多个模板实现。代码往往会分散在多个文件中,元函数更多只算是一种概念上的抽象。
如下样例所示:
//通用声明
template<typename fld1>
struct MyFunc
{
};
//针对每种类型的特化版本
template<>
struct MyFunc<int>
{
typedef CInt type;
};
template<>
struct MyFunc<char *>
{
typedef CStr type;
};
template<>
struct MyFunc<float>
{
typedef CFloat type;
};
child类
child算是本次移植最核心的一个类,也是最有意思的一个类。
在C++中定义一个函数,形参个数是固定的,而且入参顺序也必须和定义完全一致。
但child却非常“另类”,并没遵循这个基本原则,构造时,参数可以随便输。
这种用法,我只在脚本语言中看到过。
为了实现以上用法,process通过下面这个特殊的构造函数,达到此目的:
// <boost/process/detail/child_decl.hpp>
class child
{
public:
template<typename ...Args>
explicit child(Args&&...args);
}
// <boost/process/child.hpp>
template<typename ...Args>
child::child(Args&&...args)
: child(::boost::process::detail::execute_impl(std::forward<Args>(args)...)) {}
核心逻辑在execute_impl这个函数中,其位于“<boost/process/detail/execute_impl.hpp>”,此函数只是做了下字符串的转换,只要入参中包含一个unicode字符串,就将所有ansi字符串转为unicode字符串。然后,将转换后的参数转发给basic_execute_impl处理,而child构造的核心逻辑,就在此函数中。
child的构造过程
在Windows中,创建进程的API是CreateProcess。
这个函数可以说是