|
Phoenix 库有着优雅的设计,它的11个模块分为4层,模块的功能之间完全正交,没有循环依赖。低层模块不依赖高层模块,同一层的模块之间也不互相依赖。这意味着用户可以自由选用自己需要的模块,而不需要把其余部分包含进来。 Core 位于 Phoenix 库最底层(第一层)的是 Actor 模块,它是对于 Phoenix functor 的原型定义,做了一个 functor 最基本的工作:重载 operator()。 Actor 分为两种:Primitives 和 Composites,这两个模块构成 Phoenix 库的第二层。第一层和第二层称为 Phoenix 的 Core。 Primitives 提供了 Phoenix 库的功能“砖头”,而 Composites 则是“水泥”,把砖头粘接起来形成更大的功能块。 功能模块 Primitives 包括3种:Function,Operator 和 Statement。 Function --- 用来支持 Lazy functions (例如 add) Operator --- 用来支持 Lazy operators (例如 +) Statement --- 用来支持 Lazy statements (例如 if_, while_) Composites 也包括3种:Object,Scope 和 Bind。 Object --- 支持 Lazy cast(例如 static_cast_)以及对象的创建和销毁(new_ 和 delete_) Scope --- 支持作用域,本地变量和 lambda-lambda Bind --- 有点类似 boost::bind,用来把普通函数,成员函数和成员变量包装成 lazy functions 以上的6个模块位于 Phoenix 的第三层 "Lazy" STL 在 Phoenix 的最高层(第四层),是两个模仿 STL 的模块:Container(也称为 Intrinsic)和 Algorithm。 Container 提供的不是容器,而是容器的方法的 lazy 版本,例如 push_back Algorithm 就是 STL 算法的 lazy 版本 包含文件 Core #include <boost/spirit/home/phoenix/core.hpp> Function #include <boost/spirit/home/phoenix/function.hpp> Operator #include <boost/spirit/home/phoenix/operator.hpp> Statement #include <boost/spirit/home/phoenix/statement.hpp> Object #include <boost/spirit/home/phoenix/object.hpp> Scope #include <boost/spirit/home/phoenix/scope.hpp> Bind #include <boost/spirit/home/phoenix/bind.hpp> Container #include <boost/spirit/home/phoenix/container.hpp> Algorithm #include <boost/spirit/home/phoenix/algorithm.hpp> Actor Actor 是整个 Phoenix 的核心,几乎所有的组件事实上都是 Actor。但是这个处于中心地位的组件,其实是一个很简单的 concept:Actor 就是一个functor,它接受0~N个参数。 [cpp] view plaincopy template <typename Eval> struct actor : Eval { return_type operator()() const; template <typename T0> return_type operator()(T0& _0) const; template <typename T0, typename T1> return_type operator()(T0& _0, T1& _1) const; //... }; actor 的模板参数,Eval,给 actor 提供了特例化的行为。下面我们会看到,其余的 Phoenix 组件是如何在 actor 的基础上,建立起一座优美的建筑的。 Arguments #include <boost/spirit/home/phoenix/core/argument.hpp> Phoenix 的 Arguments 就是占位符,它们就像一个个数据“桶”,我们可以把数据放在里面,当作函数的参数来传递。它们的类型其实是: actor<argument<N> > N 代表第 N 个参数。Phoenix 已经预定义了几个参数占位符: actor<argument<0> > const arg1 = argument<0>(); actor<argument<1> > const arg2 = argument<1>(); actor<argument<2> > const arg3 = argument<2>(); ...... Values #include <boost/spirit/home/phoenix/core/value.hpp> Phoenix 的 value 是用来包装常数的,当我们把一个常数6传递给一个 lazy function 的时候,它实际是包装在一个 actor<value<int> > 对象里面。所有 value 的类型都是 actor<value<T> > 其中 T 自然是常数的类型,在第一篇的例子中,已经使用过类似 val(3) 这样的表达式,实际上 val 就会产生一个 actor<value<T> > 对象。 References #include <boost/spirit/home/phoenix/core/reference.hpp> Value 是包装常量的,如果要包装变量的引用,就要用到 reference,不用说,它们的类型是 actor<reference<T> > T 仍然是变量的类型。这个模块在前面没有详细的例子,请看下面代码: [cpp] view plaincopy #include <iostream> #include <vector> #include <algorithm> #include <boost/spirit/home/phoenix/core.hpp> #include <boost/spirit/home/phoenix/operator.hpp> using namespace std; using namespace boost::phoenix; using namespace boost::phoenix::arg_names; int main() { vector<int> v(10); int i = 1; std::generate(v.begin(), v.end(), ref(i)++); std::for_each(v.begin(), v.end(), cout << arg1 << " "); return 0; } 输出: 1 2 3 4 5 6 7 8 9 10 在 std::generate(v.begin(), v.end(), ref(i)++) 这一行,每一次 ref(i)++ 被调用时,它除了返回 i 的当前引用,还真正对 i 的引用做自增操作,这样 i 的值会不断增加,从而把个等差数列填入 v。
Function #include <boost/spirit/home/phoenix/function/function.hpp> Phoenix 中的 function 可不是 boost 的泛型函数指针,它是用来帮助你实现 lazy function 的。把一个自己的函数实现成 lazy function 的主要好处就是,它可以和 Phoenix 的其它模块和谐共处。这个我们先按下不表,以后用到的时候,再详细解释。 Operator #include <boost/spirit/home/phoenix/operator.hpp> 这个模块重载了几乎所有可以重载的 C++ 操作符,提供的当然是它们的 lazy 版本。事实上,正是由于有了这些 lazy operators,上一篇中这样的代码才成为可能: std::generate(v.begin(), v.end(), ref(i)++) std::for_each(v.begin(), v.end(), cout << arg1 << " "); 其中的 ++, << 都被解析为重载后的 lazy 版本。 Statement #include <boost/spirit/home/phoenix/statement.hpp> 这个模块非常有趣,它提供了许多 C++ 语句,例如 if, else, while, switch, 甚至 try...catch 的 lazy 版本。可以说,它是在用 C++ 实现一个 lazy 版本的 C++! 这样说来比较抽象,我们看看一个例子: [cpp] view plaincopy #include <algorithm> #include <boost/spirit/home/phoenix/core.hpp> #include <boost/spirit/home/phoenix/operator.hpp> #include <boost/spirit/home/phoenix/bind.hpp> #include <boost/spirit/home/phoenix/statement.hpp> using namespace std; using namespace boost::phoenix; using namespace boost::phoenix::arg_names; struct Person { string name; uint age; Person() {} Person(string const& _name, uint _age) : name(_name) , age(_age) {} void greet() { cout << "Hello, " << name << endl; } void greet(string const& _s) { cout << _s << name << endl; } }; int main() { typedef vector<Person> PersonList; PersonList pl; pl.push_back(Person("Amy", 20)); pl.push_back(Person("Rose", 21)); pl.push_back(Person("Ralph", 30)); pl.push_back(Person("Zoe", 50)); std::for_each(pl.begin(), pl.end(), if_(bind(&Person::age, arg1) < 25) [ bind(&Person::greet, arg1, "Hi, young ") ]. else_ [ bind(&Person::greet, arg1) ] ); std::for_each(pl.begin(), pl.end(), switch_(bind(&Person::age, arg1)) [ case_<30>(cout << bind(&Person::name, arg1) << ", you stand by yourself." << endl), case_<50>(cout << bind(&Person::name, arg1) << ", you know your fate." << endl), default_(cout << bind(&Person::name, arg1) << ", enjoy your life!" << endl) ] ); return 0; } 输出: Hi, young Amy Hi, young Rose Hello, Ralph Hello, Zoe Amy, enjoy your life! Rose, enjoy your life! Ralph, you stand by yourself. Zoe, you know your fate. 使用 if_, else_, switch_,case_,我们大大拓展了 for_each 的用途,实现了一些相当复杂的循环。我们可以想象,把这些 lazy statement 和其他的那么多 STL 算法结合起来,可以发挥多大的威力。 Object #include <boost/spirit/home/phoenix/object.hpp> Object 模块包括7个组件,分别对应 C++ 的构造函数,new, delete, const_cast, static_cast, dynamic_cast 和 reinterprete_cast 的 lazy 版本。 还是看个例子,如果我们想要用 vector 建立一个数字金字塔,如下面的样子,那该怎么写呢? 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 10 10 10 10 10 10 10 10 10 10 当然,写一个双层循环没任何问题,这也是大家刚学编程的时候常见的练习题,不过用 Phoenix 的 lazy constructor 结合 STL,我们可以只用一句话就搞定: [cpp] view plaincopy #include <iostream> #include <vector> #include <algorithm> #include <boost/spirit/home/phoenix/core.hpp> #include <boost/spirit/home/phoenix/operator.hpp> #include <boost/spirit/home/phoenix/object.hpp> #include <boost/spirit/home/phoenix/statement.hpp> #include <boost/spirit/home/phoenix/container.hpp> using namespace std; using namespace boost::phoenix; using namespace boost::phoenix::arg_names; int main() { typedef vector<vector<int> > Matrix; Matrix m(10); int i = 1; generate(m.begin(), m.end(), construct<vector<int> >(ref(i)++, ref(i))); for_each(m.begin(), m.end(), ( for_(ref(i) = 0, ref(i) < size(arg1), ++ref(i)) [ cout << arg1[ref(i)] << " " ], cout << val("/n") ) ); return 0; } 上面的代码里,用来把 Matrix m 初始化成一个金字塔的代码只有一句: generate(m.begin(), m.end(), construct<vector<int> >(ref(i)++, ref(i))); 其中的魔法是这样的: STL 的 generate 会在 m 的最外层循环,每一次都把 construct<vector<int> >(ref(i)++, ref(i))) 的结果赋予 m 的一个元素; construct<vector<int> > 会在栈上构造一个 vector<int>,参数为后面两个:ref(i)++ 和 ref(i); ref(i) 每次都返回一个自增的 i,第一个成为 vector<int> 的元素个数,第二个成为初始值。 于是,我们得到了这个数字金字塔,非常简洁。相反,我们的打印代码倒是挺长,而且利用了 lazy 的 for 语句。有没有什么简单一点的办法呢?有的,但是要等到我们讲完了 lazy container 和 lazy algorithm 再说。 Bind Phoenix 的 bind 和 boost.bind 差不多,但是更加健全些,我们在前面的 Statement 例子中已经用到过了。这里强调一下的是,它可以成功处理重载函数,在 Statement 例子里,bind 根据后面参数的个数,自动选择了正确的 greet 重载版本。
|