Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Users' Guide 用戶指南

Getting Started 入門
Fronts Ends: Defining Terminals and Non-Terminals of Your DSEL 前端:為你的DSEL定義終結符和非終結符
Intermediate Form: Understanding and Introspecting Expressions 中間格式:理解和窺探表達式
Back Ends: Making Expression Templates Do Useful Work 後端:讓表達式模板做有用的事
Examples 示例
Background and Resources 背景與資源
Glossary 術語

Compilers, Compiler Construction Toolkits, and Proto 編譯器,編譯器構造工具,和 Proto

Most compilers have front ends and back ends. The front end parses the text of an input program into some intermediate form like an abstract syntax tree, and the back end takes the intermediate form and generates an executable from it.
多數編譯器都具有前端和後端。前端負責將輸入的程序進行解析為一些中間格式,如抽像語法樹,而後端則接受這些中間格式並由它生成一個可執行文件。

A library built with Proto is essentially a compiler for a domain-specific embedded language (DSEL). It also has a front end, an intermediate form, and a back end. The front end is comprised of the symbols (a.k.a., terminals), members, operators and functions that make up the user-visible aspects of the DSEL. The back end is made of evaluation contexts and transforms that give meaning and behavior to the expression templates generated by the front end. In between is the intermediate form: the expression template itself, which is an abstract syntax tree in a very real sense.
一 個以 Proto 構建的庫實質上是某種領域專用嵌入式語言(DSEL)的編譯器。它也有前端、中間格式和後端。前端由符號(又稱終結符)、成員、操作符以及彌補DSEL的 用戶可見性方面的函數組成。後端則由求值上下文和變換構成,為前端所生成的表達式模板賦予意義和行為。在兩者中間則是中間格式:表達式模板本身,它是非常 現實的意義下的抽像語法樹。

To build a library with Proto, you will first decide what your interface will be; that is, you'll design a programming language for your domain and build the front end with tools provided by Proto. Then you'll design the back end by writing evaluation contexts and/or transforms that accept expression templates and do interesting things with them.
要用 Proto 來構建一個庫,你首先要決定你的接口是什麼樣的;即你要為你的問題域設計一個編程語言,並使用 Proto 所提供的工具構建前端。然後,你要通過編寫求值上下文和/或接受表達式模板並對它們做一些有意義的事情的變換操作來設計後端。

This users' guide is organized as follows. After a Getting Started guide, we'll cover the tools Proto provides for defining and manipulating the three major parts of a compiler:
這個用戶指南組織如下。在一個 入門指南 之後,我們將介紹 Proto 所提供的工具,它們用於定義和操縱一個編譯器的三個主要部分:

Front Ends 前端

How to define the aspects of your DSEL with which your users will interact directly.
如何定義你的DSEL與用戶直接交互的外貌。

Intermediate Form 中間格式

What Proto expression templates look like, how to discover their structure and access their constituents.
Proto 表達式模板看起來是什麼樣的,如何發現其結構及訪問其中的要素。

Back Ends 後端

How to define evaluation contexts and transforms that make expression templates do interesting things.
如何定義求值上下文和變換,讓表達式模板做有意義的事。

After that, you may be interested in seeing some Examples to get a better idea of how the pieces all fit together.
在此之後,你將看到一些 例子,可以對這部組成部分如何很好地合起來有更好的認識。

Getting Proto 獲取 Proto

You can get Proto by downloading proto.zip from http://www.boost-consulting.com/vault/index.php?directory=Template%20Metaprogramming, by downloading Boost (Proto is in version 1.37 and later), or by accessing Boost's SVN repository on SourceForge.net. Just go to http://svn.boost.org/trac/boost/wiki/BoostSubversion and follow the instructions there for anonymous SVN access.
你可以通過從 http://www.boost-consulting.com/vault/index.php?directory=Template%20Metaprogramming 下載 proto.zip 獲得 Proto,也可以通過下載 Boost (Proto 在版本 1.37 及後續版本中),或訪問位於 SourceForge.net 的 Boost's SVN 版本庫來獲取。請到 http://svn.boost.org/trac/boost/wiki/BoostSubversion 查閱進行匿名 SVN 訪問的指令。

Building with Proto 用 Proto 來構建

Proto is a header-only template library, which means you don't need to alter your build scripts or link to any separate lib file to use it. All you need to do is #include <boost/proto/proto.hpp>. Or, you might decide to just include the core of Proto (#include <boost/proto/core.hpp>) and whichever contexts and transforms you happen to use.
Proto 是一個只有頭文件的模板庫,這意味著你在使用它的時候不需要修改你的構建腳本或鏈接任何獨立的庫文件來。你需要做的只是 #include <boost/proto/proto.hpp>。或者,你也可以決定只包含 Proto 的核心(#include <boost/proto/core.hpp>)以及你想要使用的上下文和變換。

Requirements 要求

Proto depends on Boost. You must use either Boost version 1.34.1 or higher, or the version in SVN trunk.
Proto 依賴於 Boost。你必須使用 Boost 版本 1.34.1 或更高版本,或是 SVN trunk 中的版本。

Supported Compilers 支持的編譯器

Currently, Boost.Proto is known to work on the following compilers:
目前已知 Boost.Proto 可以使用以下編譯器:

  • Visual C++ 7.1 and higher
  • GNU C++ 3.4 and higher
  • Intel on Linux 8.1 and higher
  • Intel on Windows 9.1 and higher
[Note] Note 備註

Please send any questions, comments and bug reports to eric <at> boostpro <dot> com.

有任何問題、建議和缺陷,請報告至 eric@boostpro.com。

Proto is a large library and probably quite unlike any library you've used before. Proto uses some consistent naming conventions to make it easier to navigate, and they're described below.
Proto 是一個大型庫,可能與你所使用過的其它庫很不一樣。Proto 使用了一些一致的命名約定,使得它更容易操縱,以下對這些命名約定進行說明。

Functions 函數

All of Proto's functions are defined in the boost::proto namespace. For example, there is a function called value() defined in boost::proto that accepts a terminal expression and returns the terminal's value. 
所有的 Proto 函數定義在 boost::proto 命名空間內。例如,有一個名為 value() 的函數被定義在 boost::proto 中,它接受一個終結符表達式並返回該終結符的值。

Metafunctions 元函數

Proto defines metafunctions that correspond to each of Proto's free functions. The metafunctions are used to compute the functions' return types. All of Proto's metafunctions live in the boost::proto::result_of namespace and have the same name as the functions to which they correspond. For instance, there is a class template boost::proto::result_of::value<> that you can use to compute the return type of the boost::proto::value() function.
Proto 為每一個 Proto 普通函數定義了一個相對應的 元函數。這些元函數用於計算該函數的返回類型。所有的 Proto 元函數位於 boost::proto::result_of 名字空間內,並具有與相對應的函數相同的名字。例如,有一個類模板 boost::proto::result_of::value<>,你可以用它來計算 boost::proto::value() 函數的返回類型。

Function Objects 函數對像

Proto defines function object equivalents of all of its free functions. (A function object is an instance of a class type that defines an operator() member function.) All of Proto's function object types are defined in the boost::proto::functional namespace and have the same name as their corresponding free functions. For example, boost::proto::functional::value is a class that defines a function object that does the same thing as the boost::proto::value() free function.
Proto 為其每一個普通函數定義了等價的 函數對像(函數對象是一個定義了 operator() 成員函數的類類型的一個實例)。所有的 Proto 函數對像類型都定義在 boost::proto::functional 名字空間內,並具有與相對應的普通函數相同的名字。例如,boost::proto::functional::value 是一個類,它定義了一個函數對象,該函數對象與 boost::proto::value() 普通函數完成相同的事情。

Primitive Transforms 基本變換

Proto also defines primitive transforms -- class types that can be used to compose larger transforms for manipulating expression trees. Many of Proto's free functions have corresponding primitive transforms. These live in the boost::proto namespace and their names have a leading underscore. For instance, the transform corresponding to the value() function is called boost::proto::_value.
Proto 還定義了一些 基本變換 -- 可用於組成操縱表達式樹所用的較大型變換的類類型。多數的 Proto 普通函數都有相對應的基本變換。這些變換位於 boost::proto 名字空間內,它們的名字以下劃線打頭。例如,與 value() 函數相對應的變換名為 boost::proto::_value

The following table summarizes the discussion above:
下表概括了以上的說明:

Table 14.1. Proto Naming Conventions
表 14.1. Proto 命名約定

Entity 實體

Example 例子

Free Function 普通函數

boost::proto::value()

Metafunction 元函數

boost::proto::result_of::value<>

Function Object 函數對像

boost::proto::functional::value

Transform 變換

boost::proto::_value


Below is a very simple program that uses Proto to build an expression template and then execute it.
以下是一個非常簡單的程序,它使用 Proto 來構建一個表達式模板,然後執行它。

#include <iostream>
#include <boost/proto/proto.hpp>
#include <boost/typeof/std/ostream.hpp>
using namespace boost;

proto::terminal< std::ostream & >::type cout_ = { std::cout };

template< typename Expr >
void evaluate( Expr const & expr )
{
    proto::default_context ctx;
    proto::eval(expr, ctx);
}

int main()
{
    evaluate( cout_ << "hello" << ',' << " world" );
    return 0;
}

This program outputs the following:
該程序的輸出如下:

hello, world

This program builds an object representing the output operation and passes it to an evaluate() function, which then executes it.
這個程序構建了一個表示輸出操作的對象,並將它傳遞給一個 evaluate() 函數,後者對該操作進行執行。

The basic idea of expression templates is to overload all the operators so that, rather than evaluating the expression immediately, they build a tree-like representation of the expression so that it can be evaluated later. For each operator in an expression, at least one operand must be Protofied in order for Proto's operator overloads to be found. In the expression ...
表達式模板的基本意思是重載所有操作符,不再立即對表達式進行求值,而對該表達式構建一個樹型的表示,以便在晚些時候進行求值。對於表達式中的每一個操作符,必須至少要有一個操作數是Proto化的,這樣才可以找到 Proto 的操作符重載。在表達式 ...

cout_ << "hello" << ',' << " world"

... the Protofied sub-expression is cout_, which is the Proto-ification of std::cout. The presence of cout_ "infects" the expression, and brings Proto's tree-building operator overloads into consideration. Any literals in the expression are then Protofied by wrapping them in a Proto terminal before they are combined into larger Proto expressions.
... 中,Proto化的子表達式是 cout_,它是 std::cout 的Proto化產物。cout_ 的出現"感染"了整個表達式,進而帶來 Proto 的操作符重載,進行表達式樹的構建。在這個表達式中的任何字面值都隨後被Proto化,它們被包裝為一個 Proto 終結符,然後被組裝進更大的 Proto 表達式中。

Once Proto's operator overloads have built the expression tree, the expression can be lazily evaluated later by walking the tree. That is what proto::eval() does. It is a general tree-walking expression evaluator, whose behavior is customizable via a context parameter. The use of proto::default_context assigns the standard meanings to the operators in the expression. (By using a different context, you could give the operators in your expressions different semantics. By default, Proto makes no assumptions about what operators actually mean.)
一旦 Proto 的操作符重載構建了這棵表達式樹,這個表達式就可以在晚些時候通過遍歷來進行惰性求值。這正是 proto::eval() 所做的事情。它是一個通用的表達式樹遍歷求值器,其行為可以通過一個 上下文 參數來進行定制。proto::default_context 的使用為表達式中的操作符賦予了標準意義(通過使用不同的上下文,你可以為表達式中的操作符賦予不同的語義。缺省情況下,Proto 對於操作符的實際意義不作任何假設)。

Proto Design Philosophy  Proto的設計哲學

Before we continue, let's use the above example to illustrate an important design principle of Proto's. The expression template created in the hello world example is totally general and abstract. It is not tied in any way to any particular domain or application, nor does it have any particular meaning or behavior on its own, until it is evaluated in a context. Expression templates are really just heterogeneous trees, which might mean something in one domain, and something else entirely in a different one. 
在我們繼續往下之前,我們用上述例子來說明 Proto 的一個非常重要的設計原則。在 hello world 例子中所創建的表達式模板完全是通用和抽像的。它沒有以任何方式與任何特定的問題域或應用相結合,本身也沒有任何特定的含義或行為,直至它在一個上下文中被求值。表達式模板只是一個異類樹,可能在某個問題域中具有某種意義,而在另一個問題域中則具有完全不同的意義。

As we'll see later, there is a way to create Proto expression trees that are not purely abstract, and that have meaning and behaviors independent of any context. There is also a way to control which operators are overloaded for your particular domain. But that is not the default behavior. We'll see later why the default is often a good thing.
正如我們稍後將看到的,可以有方法創建非純抽像的 Proto 表達式樹,它具有獨立於上下文的意義和行為。也有方法為你的特定問題域控制對哪些操作符進行重載。不過這些不是缺省的行為。稍後我們將看到為什麼缺省值通常是好事。

"Hello, world" is nice, but it doesn't get you very far. Let's use Proto to build a DSEL (domain-specific embedded language) for a lazily-evaluated calculator. We'll see how to define the terminals in your mini-language, how to compose them into larger expressions, and how to define an evaluation context so that your expressions can do useful work. When we're done, we'll have a mini-language that will allow us to declare a lazily-evaluated arithmetic expression, such as (_2 - _1) / _2 * 100, where _1 and _2 are placeholders for values to be passed in when the expression is evaluated.
"Hello, world" 的例子不錯,不過它沒有把你帶得很遠。下面我們用 Proto 來為惰性求值計算器構建一個 DSEL (領域專用嵌入式語言)。我們將看到如何定義你的小型語言中的終結符,如何用它們來組成更大的表達式,以及如何定義一個求值上下文使得你的表達式可以做一 些有用的事。在我們完成了以上工作後,我們就有了一個小型語言,可以讓我們聲明一個惰性求值的算術計算器,如 (_2 - _1) / _2 * 100,其中 _1_2 為數值佔位符,在對該表達式進行求值將傳入這些數值。

Defining Terminals 定義終結符

The first order of business is to define the placeholders _1 and _2. For that, we'll use the proto::terminal<> metafunction.
要做的第一件事是定義佔位符 _1_2。為此,我們要使用 proto::terminal<> 元函數。

// Define a placeholder type 定義一個佔位符類型
template<int I> struct placeholder {}; // Define the Protofied placeholder terminals 定義Proto化的佔位符終結符
proto::terminal<placeholder<0> >::type const _1 = {{}}; proto::terminal<placeholder<1> >::type const _2 = {{}};

The initialization may look a little odd at first, but there is a good reason for doing things this way. The objects _1 and _2 above do not require run-time construction -- they are statically initialized, which means they are essentially initialized at compile time. See the Static Initialization section in the Rationale appendix for more information.
乍一看,這個初始化有些奇怪,不過這樣做是有很好的理由的。這裡的對象 _1_2 不需要運行期的構造 -- 它們是 靜態初始化的,即本質上它們是在編譯期初始化的。更多信息請見 基本原理 附錄中的 靜態初始化 一節。 

Constructing Expression Trees 構造表達式樹

Now that we have terminals, we can use Proto's operator overloads to combine these terminals into larger expressions. So, for instance, we can immediately say things like:
現在我們有終結符了,我們可以用 Proto 的操作符重載來把這些終結符組合成更大的表達式。例如,我們可以馬上寫出:

// This builds an expression template 構建一個表達式模板
(_2 - _1) / _2 * 100;

This creates an expression tree with a node for each operator. The type of the resulting object is large and complex, but we are not terribly interested in it right now.
這將構建一個表達式樹,其中每個操作符一個節點。結果對象的類型很長很複雜,不過我們現在還不需要過於擔心。

So far, the object is just a tree representing the expression. It has no behavior. In particular, it is not yet a calculator. Below we'll see how to make it a calculator by defining an evaluation context.
到目前為止,這個對象只是代表這個表達式的一棵樹。它沒有行為。具體地說,它還不是一個計算器。下面我們將看到如何通過定義一個求值上下文使它變為一個計算器。

Evaluating Expression Trees 對表達式樹求值

No doubt you want your expression templates to actually do something. One approach is to define an evaluation context. The context is like a function object that associates behaviors with the node types in your expression tree. The following example should make it clear. It is explained below.
毫無疑問,你希望你的表達式模板可以真真正正地做些什麼。一個方法是定義一個 求值上下文。這個上下文類似於一個函數對象,把行為關聯到你的表達式樹中的節點類型上。以下例子可以清楚地說明這一點。我們在後面再作解釋。

struct calculator_context
  : proto::callable_context< calculator_context const >
{
    // Values to replace the placeholders 用於替換佔位符的值
std::vector<double> args; // Define the result type of the calculator. 定義計算器的結果類型
// (This makes the calculator_context "callable".) 使calculator_context變成"可調用"的
typedef double result_type; // Handle the placeholders: 對佔位符進行處理:
template<int I> double operator()(proto::tag::terminal, placeholder<I>) const { return this->args[I]; } };

In calculator_context, we specify how Proto should evaluate the placeholder terminals by defining the appropriate overloads of the function call operator. For any other nodes in the expression tree (e.g., arithmetic operations or non-placeholder terminals), Proto will evaluate the expression in the "default" way. For example, a binary plus node is evaluated by first evaluating the left and right operands and adding the results. Proto's default evaluator uses the Boost.Typeof library to compute return types.
calculator_context 中,我們通過定義合適的函數調用操作符重載,指定 Proto 應如何對佔位符終結符進行求值。對於表達式樹中的其它節點(如算術操作或非佔位符的終結符),Proto 將以"缺省"的方式對表達式進行求值。例如,一個二元加法節點的求值方式為,首先求出左、右操作數的值,然後相加得到結果。Proto 的缺省求值器使用了 Boost.Typeof 庫來計算返回類型。

Now that we have an evaluation context for our calculator, we can use it to evaluate our arithmetic expressions, as below:
現在我們有一個求值上下文,可用於我們的計算器,我們可以用它來對我們的算術表達式進行求值,如下:

calculator_context ctx;
ctx.args.push_back(45); // the value of _1 is 45
ctx.args.push_back(50); // the value of _2 is 50
// Create an arithmetic expression and immediately evaluate it
// 創建一個算術表達式並立即對它求值
double d = proto::eval( (_2 - _1) / _2 * 100, ctx ); // This prints "10" 打印"10"
std::cout << d << std::endl;

Later, we'll see how to define more interesting evaluation contexts and expression transforms that give you total control over how your expressions are evaluated.
稍後,我們將看到如何定義更多有趣的求值上下文和表達式變換,讓你可以完全地控制如何對你的表達式進行求值。

Customizing Expression Trees 定製表達式樹

Our calculator DSEL is already pretty useful, and for many DSEL scenarios, no more would be needed. But let's keep going. Imagine how much nicer it would be if all calculator expressions overloaded operator() so that they could be used as function objects. We can do that by creating a calculator domain and telling Proto that all expressions in the calculator domain have extra members. Here is how to define a calculator domain:
我們的計算器DSEL已經相當有用,對於多數的DSEL情形,沒有更多要做的事情。不過讓我們繼續前進。想像一下,如果所有計算器表達式都重載了 operator() 以便可以它們可以像函數對像那樣來使用,這該多好。我們可以做到這一點,方法是創建一個計算器並告訴 Proto 計算器域中的所有表達式都有額外的成員。以下是如何定義一個計算器域的方法:

// Forward-declare an expression wrapper 前向聲明一個表達式包裝器
template<typename Expr> struct calculator; // Define a calculator domain. Expression within
// the calculator domain will be wrapped in the
// calculator<> expression wrapper.
// 定義一個計算器域。在此計算器域中的表達式都將被包裝在
// calculator<> 表達式包裝器中。
struct calculator_domain : proto::domain< proto::generator<calculator> > {};

The calculator<> type will be an expression wrapper. It will behave just like the expression that it wraps, but it will have extra member functions that we will define. The calculator_domain is what informs Proto about our wrapper. It is used below in the definition of calculator<>. Read on for a description.
類型 calculator<> 是一個表達式包裝器。其行為與所包裹的表達式一樣,不過它多了一個我們將要定義的成員函數。calculator_domain 則用於將我們的包裝器告知 Proto。它將在下面的 calculator<> 定義中使用。繼續看下去。

// Define a calculator expression wrapper. It behaves just like
// the expression it wraps, but with an extra operator() member
// function that evaluates the expression.
// 定義一個計算器表達式的包裝器。其行為與所包裹的表達式一樣,不過它多了一個
// 對表達式進行求值的 operator() 成員函數
template<typename Expr> struct calculator : proto::extends<Expr, calculator<Expr>, calculator_domain> { typedef proto::extends<Expr, calculator<Expr>, calculator_domain> base_type; calculator(Expr const &expr = Expr()) : base_type(expr) {} typedef double result_type; // Overload operator() to invoke proto::eval() with
// our calculator_context.
// 重載 operator(),以我們的 calculator_context 調用 proto::eval()
double operator()(double a1 = 0, double a2 = 0) const { calculator_context ctx; ctx.args.push_back(a1); ctx.args.push_back(a2); return proto::eval(*this, ctx); } };

The calculator<> struct is an expression extension. It uses proto::extends<> to effectively add additional members to an expression type. When composing larger expressions from smaller ones, Proto notes what domain the smaller expressions are in. The larger expression is in the same domain and is automatically wrapped in the domain's extension wrapper.
結構 calculator<> 是一個表達式 擴展。它用 proto::extends<> 來高效地將額外成員添加到一個表達式類型中。當從較小的表達式組成較大的表達式時,Proto 會留意到小表達式是屬於哪個域的。這樣,大表達式將被置於同一個域並自動被包裝到該域的擴展包裝器中。

All that remains to be done is to put our placeholders in the calculator domain. We do that by wrapping them in our calculator<> wrapper, as below:
剩下要做的就是將我們的佔位符也放入這個計算器域中。我們通過將它們包進我們的 calculator<> 包裝器中來做到這一點,如下:

// Define the Protofied placeholder terminals, in the
// calculator domain.
// 在計算器域中定義Proto化的佔位符終結符。
calculator<proto::terminal<placeholder<0> >::type> const _1; calculator<proto::terminal<placeholder<1> >::type> const _2;

Any larger expression that contain these placeholders will automatically be wrapped in the calculator<> wrapper and have our operator() overload. That means we can use them as function objects as follows.
任何含有這些佔位符的較大表達式都將自動被包進 calculator<> 包裝器中,並且帶有我們的 operator() 重載。這意味著我們可以像下面那樣把它們當作函數對像為使用。

double result = ((_2 - _1) / _2 * 100)(45.0, 50.0);
assert(result == (50.0 - 45.0) / 50.0 * 100));

Since calculator expressions are now valid function objects, we can use them with standard algorithms, as shown below:
由於計算器表達式現在已經是有效的函數對象了,所以我們可以將它們用於標準算法,示範如下:

double a1[4] = { 56, 84, 37, 69 };
double a2[4] = { 65, 120, 60, 70 };
double a3[4] = { 0 };

// Use std::transform() and a calculator expression
// to calculate percentages given two input sequences:
// 使用 std::transform() 和一個計算器表達式來計算給定的兩個輸入序列的百分比:
std::transform(a1, a1+4, a2, a3, (_2 - _1) / _2 * 100);

Now, let's use the calculator example to explore some other useful features of Proto.
現在,我們用這個計算器例子來展示 Proto 的其它一些有用的特性。

Detecting Invalid Expressions 檢測無效表達式

You may have noticed that you didn't have to define an overloaded operator-() or operator/() -- Proto defined them for you. In fact, Proto overloads all the operators for you, even though they may not mean anything in your domain-specific language. That means it may be possible to create expressions that are invalid in your domain. You can detect invalid expressions with Proto by defining the grammar of your domain-specific language.
你可能已經注意到,你並不需要定義重載的 operator-()operator/() -- Proto 已經為你定義了。實際上,Proto 為你重載了所有的 操作符,即使是它們在你的領域專用語言中並沒有意義。這意味著有可能創建一些在你的問題域中是無效的表達式。你可以通過定義你的領域專用語言的語法來讓 Proto 檢測無效的表達式。

For simplicity, assume that our calculator DSEL should only allow addition, subtraction, multiplication and division. Any expression involving any other operator is invalid. Using Proto, we can state this requirement by defining the grammar of the calculator DSEL. It looks as follows:
為簡單起見,假設我們的計算器DSEL只允許加、減、乘、除。任何涉及其它操作符的表達式都是無效的。使用 Proto,我們可以通過定義這個計算器DSEL的語法來聲明這些要求。如下:

// Define the grammar of calculator expressions 定義計算器表達式的語法
struct calculator_grammar : proto::or_< proto::plus< calculator_grammar, calculator_grammar > , proto::minus< calculator_grammar, calculator_grammar > , proto::multiplies< calculator_grammar, calculator_grammar > , proto::divides< calculator_grammar, calculator_grammar > , proto::terminal< proto::_ > > {};

You can read the above grammar as follows: an expression tree conforms to the calculator grammar if it is a binary plus, minus, multiplies or divides node, where both child nodes also conform to the calculator grammar; or if it is a terminal. In a Proto grammar, proto::_ is a wildcard that matches any type, so proto::terminal< proto::_ > matches any terminal, whether it is a placeholder or a literal.
你可以這樣來讀以上語法:一個表達式樹符合這個計算器語法,如果它是一個二元加法、減法、乘法或除法,且它的兩個子樹都符合這個計算器語法;或者如果它是一個終結符。在 Proto 語法中,proto::_ 是一個匹配任何類型的通配符,所以 proto::terminal< proto::_ > 匹配任何終結符,無論它是一個佔位符或字面值。

[Note] Note 備註

This grammar is actually a little looser than we would like. Only placeholders and literals that are convertible to doubles are valid terminals. Later on we'll see how to express things like that in Proto grammars.

這個語法實際上比我們想要的稍微寬鬆了一些。只有可以轉換為 double 的佔位符和字面值才是有效的終結符。稍後我們將看到如何在 Proto 語法中來表示這樣的情況。

Once you have defined the grammar of your DSEL, you can use the proto::matches<> metafunction to check whether a given expression type conforms to the grammar. For instance, we might add the following to our calculator::operator() overload:
一旦你定義了你的DSEL語法,你就可以使用 proto::matches<> 元函數來檢測一個給定的表達式類型是否符合該語法。例如,我們可以將以下代碼增加到我們的 calculator::operator() 重載中:

template<typename Expr>
struct calculator
  : proto::extends< /* ... as before ... */ >
{
    /* ... */
    double operator()(double a1 = 0, double a2 = 0) const
    {
        // Check here that the expression we are about to
// evaluate actually conforms to the calculator grammar.
// 這裡檢測我們要求值的表達式實際上是否符合計算器語法。
BOOST_MPL_ASSERT((proto::matches<Expr, calculator_grammar>)); /* ... */ } };

The addition of the BOOST_MPL_ASSERT() line enforces at compile time that we only evaluate expressions that conform to the calculator DSEL's grammar. With Proto grammars, proto::matches<> and BOOST_MPL_ASSERT() it is very easy to give the users of your DSEL short and readable compile-time errors when they accidentally misuse your DSEL.
這行新增的 BOOST_MPL_ASSERT() 在編譯期確定我們只能對符合計算器DSEL語法的表達式進行求值。通過使用 Proto 語法、proto::matches<>BOOST_MPL_ASSERT(),可以很容易地在你的DSEL用戶不小心誤用了你的DSEL時,給出簡短易讀的編譯期錯誤。

[Note] Note 備註

BOOST_MPL_ASSERT() is part of the Boost Metaprogramming Library. To use it, just #include <boost/mpl/assert.hpp>.

BOOST_MPL_ASSERT() 是 Boost Metaprogramming 庫的一部分。要使用它,只要 #include <boost/mpl/assert.hpp>.

Controlling Operator Overloads 控制操作符的重載

Grammars and proto::matches<> make it possible to detect when a user has created an invalid expression and issue a compile-time error. But what if you want to prevent users from creating invalid expressions in the first place? By using grammars and domains together, you can disable any of Proto's operator overloads that would create an invalid expression. It is as simple as specifying the DSEL's grammar when you define the domain, as shown below:
語法和 proto::matches<> 可以在用戶創建一個無效表達式時進行檢查並引發編譯期錯誤。不過,如果你想在第一時間阻止用戶創建無效的表達式工,又應如何呢?通過合用語法和領域,你可 以禁止任何可能創建無效表達式的 Proto 操作符重載。這很簡單,只要在你定義領域時指定DSEL的語法就行了,示範如下:

// Define a calculator domain. Expression within
// the calculator domain will be wrapped in the
// calculator<> expression wrapper.
// NEW: Any operator overloads that would create an
// expression that does not conform to the
// calculator grammar is automatically disabled.
// 定義一個計算器域。在該計算器域中的表達式將被包進 calculator<> 表達式包裝器中。
// 新增:任何會創建一個不符合計算器語法的操作符重載都會被自動禁止。
struct calculator_domain : proto::domain< proto::generator<calculator>, calculator_grammar > {};

The only thing we changed is we added calculator_grammar as the second template parameter to the proto::domain<> template when defining calculator_domain. With this simple addition, we disable any of Proto's operator overloads that would create an invalid calculator expression.
我們唯一修改的地方是,在定義 calculator_domain 時增加了 calculator_grammar 作為 proto::domain<> 模板的第二個模板參數。通過這一個簡單的添加,我們禁止了任何會創建出無效的計算器表達式的 Proto 操作符重載。

... And Much More  ... 還有更多

Hopefully, this gives you an idea of what sorts of things Proto can do for you. But this only scratches the surface. The rest of this users' guide will describe all these features and others in more detail.
我們希望,以上可以使你瞭解 Proto 可以為你做什麼樣的事情。不過,這只僅僅觸及到表面。這個用戶指南的其餘部分將講述所有這些特性以及其它更多細節。

Happy metaprogramming!
元編程快樂!

Here is the fun part: designing your own mini-programming language. In this section we'll talk about the nuts and bolts of designing a DSEL interface using Proto. We'll cover the definition of terminals and lazy functions that the users of your DSEL will get to program with. We'll also talk about Proto's expression template-building operator overloads, and about ways to add additional members to expressions within your domain.
以 下是比較有趣的部分:設計你自己的小型編程語言。在這一節中,我們將討論使用 Proto 來設計一個DSEL接口的相關細節。我們將涵蓋終結符定義和惰性函數,它們是你的DSEL用戶要用來寫程序的。我們也將討論 Proto 的表達式模板構建的操作符重載,以及為你的領域中的表達式增加成員的方法。

As we saw with the Calculator example from the Introduction, the simplest way to get a DSEL up and running is simply to define some terminals, as follows.
正如我們在"簡介"中看到的計算器例子,獲得一個DSEL並運行它的最簡單方法是定義一些終結符,如下。

// Define a literal integer Proto expression. 定義一個字面值整數 Proto 表達式。
proto::terminal<int>::type i = {0}; // This creates an expression template. 創建一個表達式模板。
i + 1;

With some terminals and Proto's operator overloads, you can immediately start creating expression templates.
有了一些終結符和 Proto 的操作符重載,你就可以馬上開始創建表達式模板了。

Defining terminals -- with aggregate initialization -- can be a little awkward at times. Proto provides an easier-to-use wrapper for literals that can be used to construct Protofied terminal expressions. It's called proto::literal<>.
定義終結符 -- 加上初始化 -- 通常有些難看。Proto 為字面值提供了一個更為易用的包裝器,可以用於構造Proto化的終結符表達式工。它名為 proto::literal<>

// Define a literal integer Proto expression. 定義一個字面值整數 Proto 表達式。 
proto::literal<int> i = 0; // Proto literals are really just Proto terminal expressions.
// For example, this builds a Proto expression template:
// Proto 字面值其實就是 Proto 終結符表達式工。例如,以下創建一個 Proto 表達式模板:
i + 1;

There is also a proto::lit() function for constructing a proto::literal<> in-place. The above expression can simply be written as:
還有一個 proto::lit() 函數用於就地構造 proto::literal<>。以上表達式可以簡化為:

// proto::lit(0) creates an integer terminal expression
// proto::lit(0) 創建一個整數終結符表達式
proto::lit(0) + 1;

Once we have some Proto terminals, expressions involving those terminals build expression trees for us. Proto defines overloads for each of C++'s overloadable operators in the boost::proto namespace. As long as one operand is a Proto expression, the result of the operation is a tree node representing that operation.
一旦我們有了一些 Proto 終結符之後,包含這些終結符的表達式將會為我們構建相應的表達式樹。Proto 在 boost::proto 名字空間中為C++的每一個可重載的操作符都定義了重載。只要有一個操作數是 Proto 表達式工,操作的結果就是一個表示該操作的樹節點。

[Note] Note 備註

Proto's operator overloads live in the boost::proto namespace and are found via ADL (argument-dependent lookup). That is why expressions must be "tainted" with Proto-ness for Proto to be able to build trees out of expressions.

Proto 的操作符重載位於 boost::proto 名字空間內,要通過ADL(參數依賴查找)來查找。這就是為什麼表達式必須要被Proto化對像"感染",Proto 才能構建出表達式樹的原因。

As a result of Proto's operator overloads, we can say:
作為 Proto 操作符重載的結果,我們可以寫:

-_1;        // OK, build a unary-negate tree node
_1 + 42; // OK, build a binary-plus tree node

For the most part, this Just Works and you don't need to think about it, but a few operators are special and it can be helpful to know how Proto handles them.
大多數情況下,都可以正常工作,你無需擔心它,不過有少數的操作符比較特殊,知道 Proto 是如何處理它們的可以有所幫助。

Assignment, Subscript, and Function Call Operators 賦值、下標和函數調用操作符

Proto also overloads operator=, operator[], and operator(), but these operators are member functions of the expression template rather than free functions in Proto's namespace. The following are valid Proto expressions:
Proto 也重載了 operator=, operator[], 和 operator(), 不過這些操作符是表達式模板的成員函數,而不是 Proto 名字空間下的普通函數。以下是有效的 Proto 表達式:

_1 = 5;     // OK, builds a binary assign tree node
_1[6]; // OK, builds a binary subscript tree node
_1(); // OK, builds a unary function tree node
_1(7); // OK, builds a binary function tree node
_1(8,9); // OK, builds a ternary function tree node
// ... etc.

For the first two lines, assignment and subscript, it should be fairly unsurprising that the resulting expression node should be binary. After all, there are two operands in each expression. It may be surprising at first that what appears to be a function call with no arguments, _1(), actually creates an expression node with one child. The child is _1 itself. Likewise, the expression _1(7) has two children: _1 and 7.
頭兩行,賦值和下標操作,產生的表達式節點是二元的,這應該不足為奇。畢竟,每個表達式都有兩個操作數。可能會令人驚奇的首先是不帶參數的函數調用,_1(), 實際上它將創建一個帶有一個子節點的表達式節點。這個子節點就是 _1 本身。同樣,表達式 _1(7) 有兩個子節點:_17

Because these operators can only be defined as member functions, the following expressions are invalid:
因為這些操作符只能被定義為成員函數,所以以下表達式是無效的:

int i;
i = _1;         // ERROR: cannot assign _1 to an int
int *p; p[_1]; // ERROR: cannot use _1 as an index
std::sin(_1); // ERROR: cannot call std::sin() with _1

Also, C++ has special rules for overloads of operator-> that make it useless for building expression templates, so Proto does not overload it.
還有,C++對於 operator-> 的重載具有特殊的規則,使得它不能用於構建表達式模板,因此 Proto 沒有重載它。

The Address-Of Operator 取址操作符

Proto overloads the address-of operator for expression types, so that the following code creates a new unary address-of tree node:
Proto 為表達式類型重載了取址操作符,以下代碼將會創建一個新的單參取址操作的樹節點:

&_1;    // OK, creates a unary address-of tree node

It does not return the address of the _1 object. However, there is special code in Proto such that a unary address-of node is implicitly convertible to a pointer to its child. In other words, the following code works and does what you might expect, but not in the obvious way:
它不是返回 _1 對象的地址。不過,在 Proto 中有特殊的代碼,使得一個單參取址節點可以隱式轉換為其子節點的指針。換言之,以下代碼可以工作且結果如你所想,只不過方式不太明顯而已:

typedef
    proto::terminal< placeholder<0> >::type
_1_type;

_1_type const _1 = {{}};
_1_type const * p = &_1; // OK, &_1 implicitly converted

If we limited ourselves to nothing but terminals and operator overloads, our domain-specific embedded languages wouldn't be very expressive. Imagine that we wanted to extend our calculator DSEL with a full suite of math functions like sin() and pow() that we could invoke lazily as follows.
如果我們把自己僅限制在終結符和操作符重載上,那麼我們的DSEL不會太有表達力。想像一下,我們想以一整套數學函數,如 sin()pow(),來擴展我們的計算器DSEL,以便我們可以像下面這樣對這些函數進行惰性執行。

// A calculator expression that takes one argument
// and takes the sine of it.
// 一個計算器表達式工,帶有一個參數和正弦函數。
sin(_1);

We would like the above to create an expression template representing a function invocation. When that expression is evaluated, it should cause the function to be invoked. (At least, that's the meaning of function invocation we'd like the calculator DSEL to have.) You can define sin quite simply as follows.
我們希望以上代碼可以創建一個表示這次函數調用的表達式模板。當對此表達式進行求值時,將引發對此函數的調用。(至少,這是我們所想要的計算器DSEL應具有的函數調用的意義)。你可以像下面這樣非常簡單地定義 sin。 

// "sin" is a Proto terminal containing a function pointer
// "sin" 是一個 Proto 終結符,包含一個函數指針
proto::terminal< double(*)(double) >::type const sin = {&std::sin};

In the above, we define sin as a Proto terminal containing a pointer to the std::sin() function. Now we can use sin as a lazy function. The default_context that we saw in the Introduction knows how to evaluate lazy functions. Consider the following:
在上述代碼中,我們將 sin 定義為一個含有一個 std::sin() 函數指針的 Proto 終結符。現在我們可以將 sin 作為惰性函數來使用。我們在"簡介"中看到的 default_context 知道如何對惰性函數進行求值。考慮以下代碼:

double pi = 3.1415926535;
proto::default_context ctx;
// Create a lazy "sin" invocation and immediately evaluate it
// 創建一個惰性"sin"調用並馬上對它求值
std::cout << proto::eval( sin(pi/2), ctx ) << std::endl;

The above code prints out:
以上代碼將打印出:

1

It is important to note that there is nothing special about terminals that contain function pointers. Any Proto expression has an overloaded function call operator. Consider:
有一點很重要,含有函數指針的終結符並沒有什麼特殊。任何 Proto 表達式都有一個重載的函數調用操作符。考慮:

// This compiles! 這是可以編譯的!
proto::lit(1)(2)(3,4)(5,6,7,8);

That may look strange at first. It creates an integer terminal with proto::lit(), and then invokes it like a function again and again. What does it mean? To be sure, the default_context wouldn't know what to do with it. The default_context only knows how to evaluate expressions that are sufficiently C++-like. In the case of function call expressions, the left hand side must evaluate to something that can be invoked: a pointer to a function, a reference to a function, or a TR1-style function object. That doesn't stop you from defining your own evaluation context that gives that expression a meaning. But more on that later.
這看起來很怪異。它首先以 proto::lit() 創建一個整數終結符,然後一遍一遍地象函數一樣調用它。這意味著什麼呢?可以肯定的是,default_context 不知道這要做什麼。default_context 只知道如何完全按C++的方式來對表達式求值。對於函數調用表達式,其左邊的操作數必需求值為某種可以調用的東西:函數指針、函數引用或TR1風格的函數對象。不過這並不妨礙你定義自己的求值上下文,來給這個表達式賦予某種意義。那是後話。

Making Lazy Functions, Continued 製造惰性函數(續)

Now, what if we wanted to add a pow() function to our calculator DSEL that users could invoke as follows?
現在,如果我們想把 pow() 函數增加到我們的計算器DSEL中,讓用戶可以像下面那樣來調用,又該如何?

// A calculator expression that takes one argument
// and raises it to the 2nd power
// 一個計算器表達式,帶有一個參數並計算其平方
pow< 2 >(_1);

The simple technique described above of making pow a terminal containing a function pointer doesn't work here. If pow is an object, then the expression pow< 2 >(_1) is not valid C++. pow needs to be a real function template. But it must be an unusual function; it must return an expression template.
前面所講的簡單技術,把 pow 做成一個含有函數指針的終結符,在這裡是不行的。如果 pow 是一個對象,則表達式 pow< 2 >(_1) 不是有效的C++表達式。pow 必須是一個真實的函數模板。但是它又必須是一個不平常的函數;它必須返回一個表達式模板。

Before we can write the pow() function, we need a function object that wraps an invocation of std::pow().
在我們編寫 pow() 函數之前,我們需要一個函數對像來包裝對 std::pow() 的調用。

// Define a pow_fun function object
// 定義一個 pow_fun 函數對像
template<int Exp> struct pow_fun { typedef double result_type; double operator()(double d) const { return std::pow(d, Exp); } };

Now, let's try to define a function template that returns an expression template. We'll use the proto::function<> metafunction to calculate the type of a Proto expression that represents a function call. It is analogous to proto::terminal<>. (We'll see a couple of different ways to solve this problem, and each will demonstrate another utility for defining Proto front-ends.)
現在,我們來試一下定義一個返回表達式模板的函數模板。我們將使用 proto::function<> 元函數來計算表示一個函數調用的 Proto 表達式的類型。它類似於 proto::terminal<>. (我們將看到解決這一問題的幾種不同方法,每一種示範了定義 Proto 前端的不同工具)。

// Define a lazy pow() function for the calculator DSEL.
// Can be used as: pow< 2 >(_1)
// 為計算器DSEL定義一個惰性的 pow() 函數。可以這樣來使用:pow<2>(_1)
template<int Exp, typename Arg> typename proto::function< typename proto::terminal<pow_fun<Exp> >::type , Arg const & >::type pow(Arg const &arg) { typedef typename proto::function< typename proto::terminal<pow_fun<Exp> >::type , Arg const & >::type result_type; result_type result = {{{}}, arg}; return result; }

In the code above, notice how the proto::function<> and proto::terminal<> metafunctions are used to calculate the return type: pow() returns an expression template representing a function call where the first child is the function to call and the second is the argument to the function. (Unfortunately, the same type calculation is repeated in the body of the function so that we can initialize a local variable of the correct type. We'll see in a moment how to avoid that.)
在以上代碼中,留意 proto::function<>proto::terminal<> 元函數是如何用於計算返回類型的:pow() 返回一個表示函數調用的表達式模板,其第一個子節點為要調用的函數,第二個為該函數的參數。(不幸的是,在函數體中,為了可以初始化一個正確類型的局部變量,我們重複了相同的類型計算。待會我們將看到如何避免這一點)。

[Note] Note 備註

As with proto::function<>, there are metafunctions corresponding to all of the overloadable C++ operators for calculation expression types.

跟 proto::function<> 相似,對於所有可重載的C++操作符,都有相應的元函數用於計算得到表達式的類型。

With the above definition of the pow() function, we can create calculator expressions like the one below and evaluate them using the calculator_context we implemented in the Introduction.
有了以上的 pow() 函數定義,我們就可以像下面那樣創建計算器表達式,並使用我們在"簡介"中實現的 calculator_context 來進行求值。

// Initialize a calculator context 初始化一個計算器上下文
calculator_context ctx; ctx.args.push_back(3); // let _1 be 3
// Create a calculator expression that takes one argument,
// adds one to it, and raises it to the 2nd power; and then
// immediately evaluate it using the calculator_context.
// 創建一個帶一個參數的計算器表達式,將參數加一,再求平方;然後馬上使用
// calculator_context 對它進行求值。
assert( 16 == proto::eval( pow<2>( _1 + 1 ), ctx ) );
Protofying Lazy Function Arguments 將惰性函數的參數Proto化

Above, we defined a pow() function template that returns an expression template representing a lazy function invocation. But if we tried to call it as below, we'll run into a problem.
前面,我們定義了一個 pow() 函數模板,它返回一個表達式模板,以表示一個惰性函數的調用。但是,如果我們試著按以下方式調用它,就是遇到問題。

// ERROR: pow() as defined above doesn't work when
// called with a non-Proto argument.
// 錯誤:前面所定義的pow()在使用非Proto化的參數時不能工作
pow< 2 >( 4 );

Proto expressions can only have other Proto expressions as children. But if we look at pow()'s function signature, we can see that if we pass it a non-Proto object, it will try to make it a child.
Proto 表達式只能以其它 Proto 表達式作為子節點。但如果我們看一下 pow() 的函數簽名,我們可以看到,如果我們傳給它一個非Proto化的對象,它會嘗試把它當成子節點。

template<int Exp, typename Arg>
typename proto::function<
    typename proto::terminal<pow_fun<Exp> >::type
  , Arg const & // <=== ERROR! This may not be a Proto type! 錯誤!這可能不是一個Proto類型!
>::type pow(Arg const &arg)

What we want is a way to make Arg into a Proto terminal if it is not a Proto expression already, and leave it alone if it is. For that, we can use proto::as_child(). The following implementation of the pow() function handles all argument types, expression templates or otherwise.
我們想要的就是,當 Arg 不是一個 Proto 表達式時,把它放入一個 Proto 終結符中,而如果它已經是一個 Proto 表達式則保留它。為此,我們可以使用 proto::as_child()。以下的 pow() 函數實現可以處理所有參數類型,包括表達式模板或其它類型。

// Define a lazy pow() function for the calculator DSEL. Use
// proto::as_child() to Protofy the argument, but only if it
// is not a Proto expression type to begin with!
// 為計算器DSEL定義一個惰性pow()函數。用proto::as_child()來對參數
// 進行Proto化,僅當參數不是Proto表達式類型時才進行!
template<int Exp, typename Arg> typename proto::function< typename proto::terminal<pow_fun<Exp> >::type , typename proto::result_of::as_child<Arg const>::type >::type pow(Arg const &arg) { typedef typename proto::function< typename proto::terminal<pow_fun<Exp> >::type , typename proto::result_of::as_child<Arg const>::type >::type result_type; result_type result = {{{}}, proto::as_child(arg)}; return result; }

Notice how we use the proto::result_of::as_child<> metafunction to calculate the return type, and the proto::as_child() function to actually normalize the argument.
留意一下我們是如何使用 proto::result_of::as_child<> 元函數來計算返回類型的,以及如何用 proto::as_child() 函數來實際地規範參數。

Lazy Functions Made Simple With make_expr()  用 make_expr() 簡化惰性函數

The versions of the pow() function we've seen above are rather verbose. In the return type calculation, you have to be very explicit about wrapping non-Proto types. Worse, you have to restate the return type calculation in the body of pow() itself. Proto provides a helper for building expression templates directly that handles these mundane details for you. It's called proto::make_expr(). We can redefine pow() with it as below.
上面我們看到的這個版本的 pow() 函數真的很冗長。在計算返回類型時,你必須非常明確地包裝非Proto類型。更糟的是,你必須在 pow() 的函數體中再計算一次這個返回類型。Proto 提供了一個輔助工具直接構建表達式模板,代你處理這些麻煩的細節。這個工具名為 proto::make_expr()。我們可以如下重新定義 pow()。 

// Define a lazy pow() function for the calculator DSEL.
// Can be used as: pow< 2 >(_1)
// 為計算器DSEL定義一個惰性pow()函數。可以這樣使用它:pow<2>(_1)
template<int Exp, typename Arg> typename proto::result_of::make_expr< proto::tag::function // Tag type 標籤類型
, pow_fun<Exp> // First child (by value) 第一個子節點(傳值)
, Arg const & // Second child (by reference) 第二個子節點(傳引用)
>::type pow(Arg const &arg) { return proto::make_expr<proto::tag::function>( pow_fun<Exp>() // First child (by value) 第一個子節點(傳值)
, boost::ref(arg) // Second child (by reference) 第二個子節點(傳引用)
); }

There are some things to notice about the above code. We use proto::result_of::make_expr<> to calculate the return type. The first template parameter is the tag type for the expression node we're building -- in this case, proto::tag::function, which is the tag type Proto uses for function call expressions.
以上代碼中有一些地方要注意。我們使用了 proto::result_of::make_expr<> 來計算返回的類型。第一個模板參數是我們要構建的表達式節點的標籤類型 -- 在這個例子中是 proto::tag::function,它是 Proto 中用於函數調用表達式的標籤類型。

Subsequent template parameters to proto::result_of::make_expr<> represent children nodes. If a child type is not already a Proto expression, it is made into a terminal with proto::as_child(). A type such as pow_fun<Exp> results in terminal that is held by value, whereas a type like Arg const & (note the reference) indicates that the result should be held by reference.
proto::result_of::make_expr<> 接下來的模板參數代表兩個子節點。如果某個子節點類型不是 Proto 表達式,那麼就用 proto::as_child() 將它放入一個終結符中。像 pow_fun<Exp> 這樣的類型在終結符中會以值的方式保存,而像 Arg const & (注意這個引用)這樣的類型則表示其結果以引用的方式保存。

In the function body is the runtime invocation of proto::make_expr(). It closely mirrors the return type calculation. proto::make_expr() requires you to specify the node's tag type as a template parameter. The arguments to the function become the node's children. When a child should be stored by value, nothing special needs to be done. When a child should be stored by reference, you must use the boost::ref() function to wrap the argument. Without this extra information, the proto::make_expr() function couldn't know whether to store a child by value or by reference.
在函數體中,是 proto::make_expr() 的運行期調用。它與返回類型的計算幾乎一樣。proto::make_expr() 要求你將節點的標籤類型指定為一個模板參數。傳遞給函數的參數變為節點的子節點。當一個子節點要以值的方式保存時,不需要做特殊處理。當一個子節點要以引用的方式保存時,你必須用 boost::ref() 函數來包裝這個參數。如果沒有這些額外的信息,proto::make_expr() 函數就不可能知道要以值的方式還是引用的方式來保存子節點。

In this section, we'll see how to associate Proto expressions with a domain, how to add members to expressions within a domain, and how to control which operators are overloaded in a domain.
在這一節中,我們將看到如何把 Proto 表達式與一個領域關聯起來,如果往領域內的表達式增加成員,以及如何一個領域中哪些操作符被重載。

In the Hello Calculator section, we looked into making calculator expressions directly usable as lambda expressions in calls to STL algorithms, as below:
Hello Calculator 一節中,我們已經看到可以在調用STL算法時直接創建計算器表達式並象 lambda 表達式那樣使用它,如下:

double data[] = {1., 2., 3., 4.};

// Use the calculator DSEL to square each element ... HOW?
// 使用計算器DSEL對每個元素計算平方 ... 如何?
std::transform( data, data + 4, data, _1 * _1 );

The difficulty, if you recall, was that by default Proto expressions don't have interesting behaviors of their own. They're just trees. In particular, the expression _1 * _1 won't have an operator() that takes a double and returns a double like std::transform() expects -- unless we give it one. To make this work, we needed to define an expression wrapper type that defined the operator() member function, and we needed to associate the wrapper with the calculator domain.
如果你還記得,難點在於缺省情況下 Proto 表達式並不關心它們自己的行為。它們僅僅是一些樹。具體地說,表達式 _1 * _1 並不像 std::transform() 所期望那樣帶有一個 operator() 來接受一個 double 並返回一個 double -- 除非我們給它一個。要讓它能用,我們必須定義一個表達式包裝器類型,其中定義了 operator() 成員函數,然後我們必須把這個包裝器關聯到這個計算器領域

In Proto, the term domain refers to a type that associates expressions in that domain to an expression generator. The generator is just a function object that accepts an expression and does something to it, like wrapping it in an expression wrapper.
在 Proto 中,術語 領域domain 是指一個類型,用於把該領域中的表達式關聯至一個表達式生成器。這個生成器是一個函數對象,它接受一個表達式並對它做一些事情,如把表達式包入一個表達式包裝器。

You can also use a domain to associate expressions with a grammar. When you specify a domain's grammar, Proto ensures that all the expressions it generates in that domain conform to the domain's grammar. It does that by disabling any operator overloads that would create invalid expressions.
你也可以用一個領域來將表達式與某種語法關聯起來。當你指定一個領域語法,Proto 將確保它在此領域中生成的表達式都符合該領域的語法。它是通過禁止掉一些可能創建無效表達式的操作符重載來實現這一點的。

The first step to giving your calculator expressions extra behaviors is to define a calculator domain. All expressions within the calculator domain will be imbued with calculator-ness, as we'll see.
要給你的計算器表達式賦予額外的行為,第一步是定義一個計算器域。在這個計算器域中的所有表達式都將被注入計算器的特徵,如後所見。

// A type to be used as a domain tag (to be defined below)
// 一個被用作為領域標籤的類型(稍後定義)
struct calculator_domain;

We use this domain type when extending the proto::expr<> type, which we do with the proto::extends<> class template. Here is our expression wrapper, which imbues an expression with calculator-ness. It is described below.
當我們用 proto::extends<> 類模板擴展 proto::expr<> 類型時,將會使用這個領域類型。。以下是我們的表達式包裝器,它向一個表達式注入計算器特徵。說明如下。

// The calculator<> expression wrapper makes expressions
// function objects.
// calculator<> 表達式包裝器把表達式變為函數對象。
template< typename Expr > struct calculator : proto::extends< Expr, calculator< Expr >, calculator_domain > { typedef proto::extends< Expr, calculator< Expr >, calculator_domain > base_type; calculator( Expr const &expr = Expr() ) : base_type( expr ) {} // This is usually needed because by default, the compiler-
// generated assignment operator hides extends<>::operator=
// 缺省情況下通常都需要這個,因為編譯器生成的賦值操作符會隱藏
extends<>::operator=
using base_type::operator =; typedef double result_type; // Hide base_type::operator() by defining our own which
// evaluates the calculator expression with a calculator context.
// 定義我們自己的 operator() 來隱藏 base_type::operator(),用一個計算器上下文來對計算器表達式求值。
result_type operator()( double d1 = 0.0, double d2 = 0.0 ) const { // As defined in the Hello Calculator section.
// 和 Hello Calculator 一節中的定義一樣。
calculator_context ctx; // ctx.args is a vector<double> that holds the values
// with which we replace the placeholders (e.g., _1 and _2)
// in the expression.
// ctx.args 是一個 vector<double>,保存了我們用於替換表達式中的佔位符(如 _1 和 _2)的值。
ctx.args.push_back( d1 ); // _1 gets the value of d1 _1獲得d1的值
ctx.args.push_back( d2 ); // _2 gets the value of d2 _2獲得d2的值
return proto::eval(*this, ctx ); // evaluate the expression 對表達式求值
} };

We want calculator expressions to be function objects, so we have to define an operator() that takes and returns doubles. The calculator<> wrapper above does that with the help of the proto::extends<> template. The first template to proto::extends<> parameter is the expression type we are extending. The second is the type of the wrapped expression. The third parameter is the domain that this wrapper is associated with. A wrapper type like calculator<> that inherits from proto::extends<> behaves just like the expression type it has extended, with any additional behaviors you choose to give it.
我們希望計算器表達式是一個函數對象,因此我們必須定義一個 operator() 來接受和返回 doubles。上面這個 calculator<> 包裝器在 proto::extends<> 模板的幫助下完成此事。傳給 proto::extends<> 的第一個模板參數是我們要擴展的表達式類型。第二個是被包裝表達式的類型。第三個參數是該包裝器所關聯的領域。像 calculator<> 這樣的派生自 proto::extends<> 的包裝器類型,其行為是與其所擴展的表達式類型一樣,再加上你選擇給予它的其它行為。

Although not strictly necessary in this case, we bring extends<>::operator= into scope with a using declaration. This is really only necessary if you want expressions like _1 = 3 to create a lazily evaluated assignment. proto::extends<> defines the appropriate operator= for you, but the compiler-generated calculator<>::operator= will hide it unless you make it available with the using declaration.
雖然在這裡不是嚴格必需,但是我們還是用一個 using 聲明將 extends<>::operator= 帶入此作用域。實際上,只有當你想要象 _1 = 3 這樣的表達式可以創建一個惰性求值的賦值操作時,才需要這樣做。雖然 proto::extends<> 為你定義了適當的 operator=,但是編譯器所生成的 calculator<>::operator= 會隱藏它,除非你用 using 聲明來使之可見。

Note that in the implementation of calculator<>::operator(), we evaluate the expression with the calculator_context we defined earlier. As we saw before, the context is what gives the operators their meaning. In the case of the calculator, the context is also what defines the meaning of the placeholder terminals.
注意,在 calculator<>::operator() 的實現中,我們用早先定義的 calculator_context 來對表達式進行求值。如前所述,上下文是給表達式賦予某種意義的。在這個計算器的例子中,上下文定義了佔位符終結符的意義。

Now that we have defined the calculator<> expression wrapper, we need to wrap the placeholders to imbue them with calculator-ness:
現在我們已經定義了 calculator<> 表達式包裝器,我們需要對佔位符進行包裝,為它們注入計算器特徵:

calculator< proto::terminal< placeholder<0> >::type > const _1;
calculator< proto::terminal< placeholder<1> >::type > const _2;
Retaining POD-ness with BOOST_PROTO_EXTENDS() 用 BOOST_PROTO_EXTENDS() 保留POD性質

To use proto::extends<>, your extension type must derive from proto::extends<>. Unfortunately, that means that your extension type is no longer POD and its instances cannot be statically initialized. (See the Static Initialization section in the Rationale appendix for why this matters.) In particular, as defined above, the global placeholder objects _1 and _2 will need to be initialized at runtime, which could lead to subtle order of initialization bugs.
要使用 proto::extends<>,你的擴展類型必須派生自 proto::extends<>。不幸的是,這意味著你的擴展類型不再是POD,且其實例不能被靜態初始化。(為什麼會這樣,請見附錄 原理靜態初始化 一節)。具體地說,按以上定義,全局佔位符 _1_2 必須在運行期初始化,這有可能導致微妙的初始化順序問題。

There is another way to make an expression extension that doesn't sacrifice POD-ness : the BOOST_PROTO_EXTENDS() macro. You can use it much like you use proto::extends<>. We can use BOOST_PROTO_EXTENDS() to keep calculator<> a POD and our placeholders statically initialized.
有另一個方法可以讓表達式擴展無需犧牲POD特性:宏 BOOST_PROTO_EXTENDS()。你可以像使用 proto::extends<> 那樣使用它。我們可以用 BOOST_PROTO_EXTENDS() 來使 calculator<> 保持為一個 POD 且我們的佔位符可以被靜態初始化。

// The calculator<> expression wrapper makes expressions
// function objects.
// calculator<> 表達式包裝器把表達式變為函數對象。
template< typename Expr > struct calculator { // Use BOOST_PROTO_EXTENDS() instead of proto::extends<> to
// make this type a Proto expression extension.
// 使用 BOOST_PROTO_EXTENDS() 代替 proto::extends<> 來讓這個類型成為Proto表達式擴展。
BOOST_PROTO_EXTENDS(Expr, calculator<Expr>, calculator_domain) typedef double result_type; result_type operator()( double d1 = 0.0, double d2 = 0.0 ) const { /* ... as before 如前 ... */ } };

With the new calculator<> type, we can redefine our placeholders to be statically initialized:
用這個新的 calculator<> 類型,我們可以重新定義我們的佔位符,並進行靜態初始化:

calculator< proto::terminal< placeholder<0> >::type > const _1 = {{{}}};
calculator< proto::terminal< placeholder<1> >::type > const _2 = {{{}}};

We need to make one additional small change to accommodate the POD-ness of our expression extension, which we'll describe below in the section on expression generators.
我們還需要一個小的修改來提供我們的表達式擴展的POD性,我們將在本節稍後的表達生成器中描述這一點。

What does BOOST_PROTO_EXTENDS() do? It defines a data member of expression type being extended; some nested typedefs that Proto requires; operator=, operator[] and operator() overloads for building expression templates; and a nested result<> template for calculating the return type of operator(). In this case, however, the operator() overloads and the result<> template are not needed because we are defining our own operator() in the calculator<> type. Proto provides additional macros for finer control over which member functions are defined. We could improve our calculator<> type as follows:
BOOST_PROTO_EXTENDS() 究竟做了些什麼呢?它定義了一個要擴展的表達式類型的數據成員;一些 Proto 所需的嵌套 typedef;重載 operator=, operator[]operator() 以構建表達式模板;以及一個嵌套的 result<> 模板用於計算 operator() 的返回類型。不過在這個例子中,並不需要 operator() 重載和 result<> 模板,因為我們在 calculator<> 類型中定義了我們自己的 operator()Proto 提供了其它的宏,以便更好地控制哪些成員函數要定義。我們可以如下改進我們的 calculator<> 類型:

// The calculator<> expression wrapper makes expressions
// function objects.
// calculator<> 表達式包裝器把表達式變為函數對象。
template< typename Expr > struct calculator { // Use BOOST_PROTO_BASIC_EXTENDS() instead of proto::extends<> to
// make this type a Proto expression extension:
// 使用 BOOST_PROTO_EXTENDS() 代替 proto::extends<> 來讓這個類型成為Proto表達式擴展。
 BOOST_PROTO_BASIC_EXTENDS(Expr, calculator<Expr>, calculator_domain) // Define operator[] to build expression templates:
// 定義 operator[] 來構建表達式模板:
BOOST_PROTO_EXTENDS_SUBSCRIPT() // Define operator= to build expression templates:
// 定義 operator= 來構建表達式模板:
BOOST_PROTO_EXTENDS_ASSIGN() typedef double result_type; result_type operator()( double d1 = 0.0, double d2 = 0.0 ) const { /* ... as before 如前 ... */ } };

Notice that we are now using BOOST_PROTO_BASIC_EXTENDS() instead of BOOST_PROTO_EXTENDS(). This just adds the data member and the nested typedefs but not any of the overloaded operators. Those are added separately with BOOST_PROTO_EXTENDS_ASSIGN() and BOOST_PROTO_EXTENDS_SUBSCRIPT(). We are leaving out the function call operator and the nested result<> template that could have been defined with Proto's BOOST_PROTO_EXTENDS_FUNCTION() macro.
注意,現在我們用 BOOST_PROTO_BASIC_EXTENDS() 來替代 BOOST_PROTO_EXTENDS()。它只增加數據成員和嵌套 typedefs 而不重載任何操作符。操作符是通過 BOOST_PROTO_EXTENDS_ASSIGN()BOOST_PROTO_EXTENDS_SUBSCRIPT() 單獨添加的。我們省去了函數調用操作符和嵌套的 result<> 模板,它們可以用 Proto 的 BOOST_PROTO_EXTENDS_FUNCTION() 宏來定義。

In summary, here are the macros you can use to define expression extensions, and a brief description of each.
總之,以下是你可以用來定義表達式擴展的宏,以及各個宏的簡要說明。

Table 14.2. Expression Extension Macros
表 14.2. 表達式擴展宏

Macro 宏

Purpose 用途

BOOST_PROTO_BASIC_EXTENDS(
    expression
  , extension
  , domain
)

Defines a data member of type expression and some nested typedefs that Proto requires.
定義一個類型為 expression 的數據成員以及 Proto 所需的一些嵌套 typedefs。

BOOST_PROTO_EXTENDS_ASSIGN()

Defines operator=. Only valid when preceded by BOOST_PROTO_BASIC_EXTENDS().
定義 operator=。僅當在 BOOST_PROTO_BASIC_EXTENDS() 之後有效。

BOOST_PROTO_EXTENDS_SUBSCRIPT()

Defines operator[]. Only valid when preceded by BOOST_PROTO_BASIC_EXTENDS().
定義 operator[]。僅當在 BOOST_PROTO_BASIC_EXTENDS() 之後有效。

BOOST_PROTO_EXTENDS_FUNCTION()

Defines operator() and a nested result<> template for return type calculation. Only valid when preceded by BOOST_PROTO_BASIC_EXTENDS().
定義 operator() 和嵌套的 result<> 模板用於返回類型計算。僅當在 BOOST_PROTO_BASIC_EXTENDS() 之後有效。

BOOST_PROTO_EXTENDS(
    expression
  , extension
  , domain
)

Equivalent to:
相當於:

BOOST_PROTO_BASIC_EXTENDS(expression, extension, domain)
BOOST_PROTO_EXTENDS_ASSIGN()
BOOST_PROTO_EXTENDS_SUBSCRIPT()
BOOST_PROTO_EXTENDS_FUNCTION()


The last thing that remains to be done is to tell Proto that it needs to wrap all of our calculator expressions in our calculator<> wrapper. We have already wrapped the placeholders, but we want all expressions that involve the calculator placeholders to be calculators. We can do that by specifying an expression generator when we define our calculator_domain, as follows:
最後剩下要做的一件事就是,告知 Proto 它必須將我們所有的計算器表達式包入 calculator<> 包裝器。我們已經包裝了佔位符,但是我們想要包含了這個計算器佔位符的所有表達式都是計算器。我們可以通過在定義我們的 calculator_domain 時指定一個表達式生成器來實現這一點,如下:

// Define the calculator_domain we forward-declared above.
// Specify that all expression in this domain should be wrapped
// in the calculator<> expression wrapper.
// 定義我們先前已聲明的 calculator_domain。指定該領域內的所有表達式都
// 要被包入 calculator<> 表達式包裝器中。
struct calculator_domain : proto::domain< proto::generator< calculator > > {};

The first template parameter to proto::domain<> is the generator. "Generator" is just a fancy name for a function object that accepts an expression and does something to it. proto::generator<> is a very simple one --- it wraps an expression in the wrapper you specify. proto::domain<> inherits from its generator parameter, so all domains are themselves function objects.
傳給 proto::domain<> 的第一個模板參數是生成器。"生成器Generator" 一個函數對象的特別名字,這個函數對像接受一個表達式並對它做一些事情。proto::generator<> 是一個非常簡單的生成器 --- 它把一個表達式包入你指定的某個包裝器。proto::domain<> 派生自它的生成器參數,因此所有領域本身都是函數對象。

If we used BOOST_PROTO_EXTENDS() to keep our expression extension type POD, then we need to use proto::pod_generator<> instead of proto::generator<>, as follows:
如果我們使用 BOOST_PROTO_EXTENDS() 來讓我們的表達式擴展類型保持為 POD,則我們需要用 proto::pod_generator<> 代替 proto::generator<>,如下:

// If calculator<> uses BOOST_PROTO_EXTENDS() instead of 
// use proto::extends<>, use proto::pod_generator<> instead
// of proto::generator<>.
// 如果 calculator<> 使用 BOOST_PROTO_EXTENDS() 代替 proto::extends(),
// 則使用 proto::pod_generator<> 代替 proto::generator<>。
struct calculator_domain : proto::domain< proto::pod_generator< calculator > > {};

After Proto has calculated a new expression type, it checks the domains of the child expressions. They must match. Assuming they do, Proto creates the new expression and passes it to Domain ::operator() for any additional processing. If we don't specify a generator, the new expression gets passed through unchanged. But since we've specified a generator above, calculator_domain::operator() returns calculator<> objects.
在 Proto 計算出一個新的表達式類型後,它要檢查其子表達式的領域。它們必須相互匹配。如果確實如此,則 Proto 創建新的表達式並將它傳遞給 Domain ::operator() 進行其它處理。如果我們不指定生成器,則新的表達式將被原樣傳遞。不過由於我們指定了上面的生成器,所以 calculator_domain::operator() 將返回 calculator<> 對象。

Now we can use calculator expressions as function objects to STL algorithms, as follows:
現在我們可以把計算器表達式作為函數對像用於STL算法了,如下:

double data[] = {1., 2., 3., 4.};

// Use the calculator DSEL to square each element ... WORKS! :-)
// 使用計算器DSEL將每個元素求平方 ... 可用!:-)
std::transform( data, data + 4, data, _1 * _1 );

By default, Proto defines every possible operator overload for Protofied expressions. This makes it simple to bang together a DSEL. In some cases, however, the presence of Proto's promiscuous overloads can lead to confusion or worse. When that happens, you'll have to disable some of Proto's overloaded operators. That is done by defining the grammar for your domain and specifying it as the second parameter of the proto::domain<> template.
缺 省情況下,Proto 為Proto化的表達式定義了每一個可能的操作符重載。這樣可以簡化 bang together a DSEL。但是,在某些情況下,Proto 的混雜重載可能導致混亂或更糟的結果。這時,你必須禁止 Proto 重載某些操作符。實現的方法是,為你的領域定義語法並將該語法指定為 proto::domain<> 模板的第二個參數。

In the Hello Calculator section, we saw an example of a Proto grammar, which is repeated here:
Hello Calculator 一節中,我們已看過一個 Proto 語法的例子,重複如下:

// Define the grammar of calculator expressions
// 定義計算器表達式的語法
struct calculator_grammar : proto::or_< proto::plus< calculator_grammar, calculator_grammar > , proto::minus< calculator_grammar, calculator_grammar > , proto::multiplies< calculator_grammar, calculator_grammar > , proto::divides< calculator_grammar, calculator_grammar > , proto::terminal< proto::_ > > {};

We'll have much more to say about grammars in subsequent sections, but for now, we'll just say that the calculator_grammar struct describes a subset of all expression types -- the subset that comprise valid calculator expressions. We would like to prohibit Proto from creating a calculator expression that does not conform to this grammar. We do that by changing the definition of the calculator_domain struct.
在後續章節中,我們將會講述關於語法的更多內容,不過現在,我們只講一下 calculator_grammar 結構,它描述了所有表達式類型的一個子集 -- 這個子集組成了有效的計算器表達式。我們要禁止 Proto 創建一個不符合該語法的計算器表達式。我們通過修改 calculator_domain 結構的定義來實現這一點。

// Define the calculator_domain. Expressions in the calculator
// domain are wrapped in the calculator<> wrapper, and they must
// conform to the calculator_grammar:
// 定義 calculator_domain。計算器領域中的表達式要被包入 calculator<> 包裝器,
// 且必須符合 calculator_grammar:
struct calculator_domain : proto::domain< proto::generator< calculator >, calculator_grammar > {};

The only new addition is calculator_grammar as the second template parameter to the proto::domain<> template. That has the effect of disabling any of Proto's operator overloads that would create an invalid calculator expression.
新增的部分是把 calculator_grammar 作為第二個模板參數傳給 proto::domain<> 模板。其結果是禁止任何會創建無效計算器表達式的 Proto 操作符重載。

Another common use for this feature would be to disable Proto's unary operator& overload. It may be surprising for users of your DSEL that they cannot take the address of their expressions! You can very easily disable Proto's unary operator& overload for your domain with a very simple grammar, as below:
這一特性的另外一個常見用途是,禁止 Proto 的單參數 operator& 重載。可能會令你的DSEL用戶意外的是,他們不能獲得他們的表達式的地址!你可以用非常簡單的語法很容易地為你的領域禁止掉 Proto 的單參數 operator& 重載,如下:

// For expressions in my_domain, disable Proto's
// unary address-of operator.
// 對於 my_domain 中的表達式,禁止 Proto 的單參數求址操作符。
struct my_domain : proto::domain< proto::generator< my_wrapper > // A simple grammar that matches any expression that
// is not a unary address-of expression.
// 一個簡單的語法,匹配所有不是單參數求址表達式的表達式。
, proto::not_< proto::address_of< _ > > > {};

The type proto::not_< proto::address_of< _ > > is a very simple grammar that matches all expressions except unary address-of expressions. In the section describing Proto's intermediate form, we'll have much more to say about grammars.
類型 proto::not_< proto::address_of< _ > > 是一個非常簡單的語法,它匹配除了單參數求址表達式以外的所有表達式。在講述 Proto 中間格式的一節中,我們將講述關於語法的更多內容。

By now, you know a bit about how to build a front-end for your DSEL "compiler" -- you can define terminals and functions that generate expression templates. But we haven't said anything about the expression templates themselves. What do they look like? What can you do with them? In this section we'll see.
現在,你已經知道如何為你的DSEL"編譯器"構建一個前端 -- 你可以定義終結符和生成表達式模板的函數。不過我們還沒有講到關於表達式模板本身的東西。它們看起來是怎樣的呢?你可以對它們做些什麼呢?在本節中,我們將會看到這些內容。

The expr<> Type  類型 expr<>

All Proto expressions are an instantiation of a template called expr<> (or a wrapper around such an instantiation). When we define a terminal as below, we are really initializing an instance of the proto::expr<> template.
所有 Proto 表達式都是一個名為 expr<> 的模板的實例(或是包含該實例的一個包裝器)。當我們如下定義一個終結符時,我們實際上是初始化了一個 proto::expr<> 模板的實例。

// Define a placeholder type 定義一個佔位符類型
template<int I> struct placeholder {}; // Define the Protofied placeholder terminal 定義Proto化的佔位符終結符
proto::terminal< placeholder<0> >::type const _1 = {{}};

The actual type of _1 looks like this:
_1 的實際類型如下:

proto::expr< proto::tag::terminal, proto::term< placeholder<0> >, 0 >

The proto::expr<> template is the most important type in Proto. Although you will rarely need to deal with it directly, it's always there behind the scenes holding your expression trees together. In fact, proto::expr<> is the expression tree -- branches, leaves and all.
proto::expr<> 模板是 Proto 中最重要的類型。雖然你很少要直接處理它,但是它總是在幕後把你的表達式樹保持在一起。事實上,proto::expr<> 就是表達式樹 -- 分支、葉子和所有。

The proto::expr<> template makes up the nodes in expression trees. The first template parameter is the node type; in this case, proto::tag::terminal. That means that _1 is a leaf-node in the expression tree. The second template parameter is a list of child types, or in the case of terminals, the terminal's value type. Terminals will always have only one type in the type list. The last parameter is the arity of the expression. Terminals have arity 0, unary expressions have arity 1, etc.
proto::expr<> 模板負責拼湊表達式樹中的節點。第一個模板參數是節點的類型;在這個例子中是 proto::tag::terminal。這意味著 _1 是表達式樹中的一個葉節點。第二個模板參數是一個子節點類型的列表,或者在終結符的情況下,為終結符的值類型。在這個類型列表中,終結符永遠只有一個類型。最後一個參數是表達式的arity。終結符的arity為0,單參數表達式的arity為1,等等。

The proto::expr<> struct is defined as follows:
proto::expr<> 結構定義如下:

template< typename Tag, typename Args, long Arity = Args::arity >
struct expr;

template< typename Tag, typename Args >
struct expr< Tag, Args, 1 >
{
    typedef typename Args::child0 proto_child0;
    proto_child0 child0;
    // ...
};

The proto::expr<> struct does not define a constructor, or anything else that would prevent static initialization. All proto::expr<> objects are initialized using aggregate initialization, with curly braces. In our example, _1 is initialized with the initializer {{}}. The outer braces are the initializer for the proto::expr<> struct, and the inner braces are for the member _1.child0 which is of type placeholder<0>. Note that we use braces to initialize _1.child0 because placeholder<0> is also an aggregate.
proto::expr<> 結構沒有定義構造函數,或其它任何東西,以防止被靜態初始化。所有 proto::expr<> 對象都使用帶花括號的 聚集初始化 來初始化。在我們的例子中,_1 用初始化器 {{}} 來初始化。外面一層花括號是用於 proto::expr<> 結構的初始化器,裡面一層花括號則是用於成員 _1.child0,其類型為 placeholder<0>。注意,我們使用花括號來初始化 _1.child0,因為 placeholder<0> 也是一個聚集。

Building Expression Trees 構建表達式樹

The _1 node is an instantiation of proto::expr<>, and expressions containing _1 are also instantiations of proto::expr<>. To use Proto effectively, you won't have to bother yourself with the actual types that Proto generates. These are details, but you're likely to encounter these types in compiler error messages, so it's helpful to be familiar with them. The types look like this:
節點 _1proto::expr<> 的一個實例,包含 _1 的表達式也是 proto::expr<> 的實例。要有效地使用 Proto,你並不必操心 Proto 生成的實際類型。 它們只是實現細節而已,不過你很可能會在編譯器的錯誤信息中碰到這些類型,所以,熟悉一下它們也是很有用的。這些類型就像以下這樣:

// The type of the expression -_1 表達式 -_1 的類型
typedef proto::expr< proto::tag::negate , proto::list1< proto::expr< proto::tag::terminal , proto::term< placeholder<0> > , 0 > const & > , 1 > negate_placeholder_type; negate_placeholder_type x = -_1; // The type of the expression _1 + 42 表達式 _1 + 42 的類型
typedef proto::expr< proto::tag::plus , proto::list2< proto::expr< proto::tag::terminal , proto::term< placeholder<0> > , 0 > const & , proto::expr< proto::tag::terminal , proto::term< int const & > , 0 > > , 2 > placeholder_plus_int_type; placeholder_plus_int_type y = _1 + 42;

There are a few things to note about these types:
對於這些類型,有幾點要注意:

  1. Terminals have arity 0, unary expressions have arity 1 and binary expressions have arity 2.
    終結符的arity為0,單參數表達式的arity為1,二元表達式的arity為2。
  2. When one Proto expression is made a child node of another Proto expression, it is held by reference, even if it is a temporary object. This last point becomes important later.
    當一個 Proto 表達式作為另一個 Proto 表達式的子節點時,它是以引用方式被持有的,即使它是一個臨時對像。最後一點很重要。
  3. Non-Proto expressions, such as the integer literal, are turned into Proto expressions by wrapping them in new expr<> terminal objects. These new wrappers are not themselves held by reference, but the object wrapped is. Notice that the type of the Protofied 42 literal is int const & -- held by reference.
    非 Proto 表達式,如整數字面值,通過把它們包入一個新的 expr<> 終結符對像變為 Proto 表達式。這些新的包裝器本身不是以引用方式持有的,但被包裝的對象。注意,Proto化的 42 字面值的類型是 int const & -- 是以引用方式持有的。

The types make it clear: everything in a Proto expression tree is held by reference. That means that building an expression tree is exceptionally cheap. It involves no copying at all.
很顯然:在一個 Proto 表達式樹中的任何東西都是以引用方式持有的。這意味著構建一個表達式樹代價非常低。它根本不進行複製。

[Note] Note 備註

An astute reader will notice that the object y defined above will be left holding a dangling reference to a temporary int. In the sorts of high-performance applications Proto addresses, it is typical to build and evaluate an expression tree before any temporary objects go out of scope, so this dangling reference situation often doesn't arise, but it is certainly something to be aware of. Proto provides utilities for deep-copying expression trees so they can be passed around as value types without concern for dangling references.
聰明的讀者可能會注意到,上面所定義的對象 y 會持有一個對臨時 int 的懸空引用。在各種使用 Proto 的高性能應用程序中,典型的方法是在任何一個臨時對像離開其範圍之前,對一個表達式樹構建並求值完成,所以這種懸空引用的情形通常不會發生,但這一點還是 必須要知道的。Proto 提供了對表達式樹進行深複製的工具,所以表達式樹可以作為值類型進行傳遞而無需擔心懸空的引用。

After assembling an expression into a tree, you'll naturally want to be able to do the reverse, and access a node's children. You may even want to be able to iterate over the children with algorithms from the Boost.Fusion library. This section shows how.
把一個表達式組裝成一棵樹之後,很自然你會想能夠做相反的動作,訪問某個子節點。甚至你可能會想能夠用 Boost.Fusion 庫的算法來遍歷子節點。本節將展示如何實現這一點。

tag_of<>

A node in an expression tree is nothing more than a collection of child nodes and a tag type. You can access the tag type of any Proto expression type Expr directly as typename Expr::proto_tag, or you can use the proto::tag_of<> metafunction, as shown below:
在一個表達式樹中的節點只不過是一些子節點和一個標籤類型的組合。你可以直接訪問任何 Proto 表達式類型 Expr 的標籤類型,用 typename Expr::proto_tag,或者用 proto::tag_of<> 元函數,如下所示:

template<typename Expr>
typename proto::result_of::tag_of<Expr>::type
get_tag_of(Expr const &)
{
    // Tag types are required to be default-constructible 標籤類型要求是可以缺省構造的
return typename proto::result_of::tag_of<Expr>::type(); } proto::terminal<int>::type const i = {42}; // Addition nodes have the "plus" tag type: 加法節點具有"plus"標籤類型:
proto::tag::plus plus_tag = get_tag_of( i + 2 );
child_c()

Each node in an expression tree corresponds to an operator in an expression, and the children correspond to the operands, or arguments of the operator. To access them, you can use the proto::child_c() function template, as demonstrated below:
表達式樹中的每一個節點對應於表達式中的一個操作符,而子節點則對應於操作數或操作符的參數。要訪問它們,你可以用 proto::child_c() 函數模板,示範如下:

proto::terminal<int>::type i = {42};

// Get the 0-th operand of an addition operation: 取出加法操作的第0個操作數:
proto::terminal<int>::type &ri = proto::child_c<0>( i + 2 ); // Assert that we got back what we put in: 斷言我們取回了所放入的:
assert( &i == &ri );

You can use the result_of::child_c<> metafunction to get the type of the Nth child of an expression node. Usually you don't care to know whether a child is stored by value or by reference, so when you ask for the type of the Nth child of an expression Expr, you get the child's type after references and cv-qualifiers have been stripped from it.
你可以用 result_of::child_c<> 元函數取出某個表達式節點的第N個子節點的類型。通常你不必關心一個子節點是以值方式還是以引用方式保存的,因此當你詢問一個表達式 Expr 的第N個子節點的類型時,你所得到的子節點類型是在引用和cv限定符被摘取之後的類型。

template<typename Expr>
void test_result_of_child_c(Expr const &expr)
{
    typedef typename proto::result_of::child_c<Expr, 0>::type type;

    // ::type is a non-cv qualified, non-reference   ::type是一個非cv限定的,非引用的類型
BOOST_MPL_ASSERT((is_same< type, terminal<int>::type>)); } // ...
terminal<int>::type i = {42}; test_result_of_child_c( i + 2 );

However, if you ask for the type of the Nth child of Expr & or Expr const & (note the reference), the result type will be a reference, regardless of whether the child is actually stored by reference or not. If you need to know exactly how the child is stored in the node, whether by reference or by value, you can use fusion::result_of::value_at<Expr, N>::type. The following table summarizes the behavior of the child_c<> metafunction.
但是,如果你詢問的是 Expr &Expr const & (留意其中的引用)的第N個子節點的類型,則結果類型將會是一個引用,無論這個子節點實際上是否以引用的方式保存。如果你需要精確地知道子節點是如何保存的,是值方式還是引用方式,你可以用 fusion::result_of::value_at<Expr, N>::type。下表總結了 child_c<> 元函數的行為。

Table 14.3. Accessing Child Types
表14.3. 訪問子節點的類型

Metafunction Invocation 元函數調用

When the Child Is ... 子節點是...

The Result Is ... 結果是...

proto::result_of::child_c<Expr, N>::type

T

T

T &

T

T const &

T

proto::result_of::child_c<Expr &, N>::type

T

T &

T &

T &

T const &

T const &

proto::result_of::child_c<Expr const &, N>::type

T

T const &

T &

T &

T const &

T const &

fusion::result_of::value_at<Expr, N>::type

T

T

T &

T &

T const &

T const &


value(), child(), left(), and right()

Most operators in C++ are unary or binary. For that reason, accessing the only operand, or the left and right operands, are very common operations. For this reason, Proto provides the proto::child(), proto::left(), and proto::right() functions. proto::child() and proto::left() are synonymous with child_c<0>(), and proto::right() is synonymous with child_c<1>().
C++中的多數操作符都是單參數或雙參數的。因此,訪問唯一的操作數,或訪問左、右操作數,是非常常見的操作。為此,Proto 提供了 proto::child(), proto::left(), 和 proto::right() 函數。proto::child()proto::left() 相當於 child_c<0>(),而 proto::right() 則相當於 child_c<1>()

Another very common operation is accessing the value stored within a Proto terminal. You can use the proto::value() function for that.
另一個很常見的操作是,訪問保存在一個 Proto 終結符內的值。你可以使用 proto::value() 函數。

There are also result_of::child<>, result_of::left<>, and result_of::right<> metafunctions that merely forward to their result_of::child_c<> counterparts. Likewise, there is a result_of::value<> metafunction that returns the type of the value stored in a terminal node.
還有,result_of::child<>, result_of::left<>, and result_of::right<> 元函數只是前轉至相應的 result_of::child_c<>。同樣, result_of::value<> 元函數則返回保存在一個終結符節點中的值的類型。

Expression Nodes as Fusion Sequences 作為Fusion序列的表達式節點

Proto expression nodes are valid Fusion random-access sequences of their child nodes. That means you can apply Fusion algorithms to them, transform them, apply Fusion filters and views to them, and access their elements using fusion::at(). The things Fusion can do to heterogeneous sequences are beyond the scope of this users' guide, but below is a simple example. It takes a lazy function invocation like fun(1,2,3,4) and uses Fusion to print the function arguments in order.
Proto 表達式節點是其子節點的有效 Fusion 隨機訪問序列。這意味著你可以對它們應用 Fusion 算法、轉換它們,應用 Fusion 過濾器和查看它們,以及使用 fusion::at() 訪問它們的元素。這些 Fusion 可以對異類型序列的事情已經超出本用戶指南的討論範圍,下面給出一個簡單的例子。該例子接受一個類似於 fun(1,2,3,4) 的惰性函數調用,並使用 Fusion 來按順序打印函數的參數。

struct display
{
    template<typename T>
    void operator()(T const &t) const
    {
        std::cout << t << std::endl;
    }
};

struct fun_t {};
proto::terminal<fun_t>::type const fun = {{}};

// ...
fusion::for_each( fusion::transform( // pop_front() removes the "fun" child pop_front()刪除"fun"子節點
fusion::pop_front(fun(1,2,3,4)) // Extract the ints from the terminal nodes 從終結符節點取出整數
, proto::functional::value() ) , display() );

Recall from the Introduction that types in the proto::functional namespace define function objects that correspond to Proto's free functions. So proto::functional::value() creates a function object that is equivalent to the proto::value() function. The above invocation of fusion::for_each() displays the following:
回想一下"簡介"中的介紹,proto::functional 名字空間中的類型定義了與 Proto 普通函數相對應的函數對象。因此 proto::functional::value() 創建一個等價於 proto::value() 函數的函數對象。以上 fusion::for_each() 調用將顯示以下內容:

1
2
3
4
Flattening Proto Expression Tress 將 Proto 表達式樹攤平

Imagine a slight variation of the above example where, instead of iterating over the arguments of a lazy function invocation, we would like to iterate over the terminals in an addition expression:
想像一下以上例子的輕微變化,不遍歷惰性函數調用的參數,而是遍歷另一個表達式中的終結符:

proto::terminal<int>::type const _1 = {1};

// ERROR: this doesn't work! Why? 錯誤:不能這樣做!為什麼?
fusion::for_each( fusion::transform( _1 + 2 + 3 + 4 , proto::functional::value() ) , display() );

The reason this doesn't work is because the expression _1 + 2 + 3 + 4 does not describe a flat sequence of terminals --- it describes a binary tree. We can treat it as a flat sequence of terminals, however, using Proto's proto::flatten() function. proto::flatten() returns a view which makes a tree appear as a flat Fusion sequence. If the top-most node has a tag type T, then the elements of the flattened sequence are the child nodes that do not have tag type T. This process is evaluated recursively. So the above can correctly be written as:
不能這樣做的原因是,因為表達式 _1 + 2 + 3 + 4 並不表示為各個終結符的一個扁平序列 --- 它表示為一個二叉樹。不過,我們可以通過用 Proto 的 proto::flatten() 函數將它視為終結符的扁平序列。proto::flatten() 返回一個視圖,使一棵樹看起來像一個扁平的 Fusion 序列。如果最頂部的節點具有標籤類型 T,則扁平化序列的元素為那些不具有標籤類型 T 的子節點。這個過程是遞歸進行的。所以以上例子可以正確地寫為:

proto::terminal<int>::type const _1 = {1};

// OK, iterate over a flattened view  好的,遍歷一個扁平視圖
fusion::for_each( fusion::transform( proto::flatten(_1 + 2 + 3 + 4) , proto::functional::value() ) , display() );

The above invocation of fusion::for_each() displays the following:
以上 fusion::for_each() 調用將顯示如下:

1
2
3
4

The following table lists the overloadable C++ operators, the Proto tag types for each, and the name of the metafunctions for generating the corresponding Proto expression types. And as we'll see later, the metafunctions are also usable as grammars for matching such nodes, as well as pass-through transforms.
下表列出了可重載的C++操作符、各個 Proto 標籤類型,以及生成相應的 Proto 表達式類型的元函數名字。我們稍後將看到,這些元函數也可用作匹配這些節點的語法,以及傳遞給變換操作。

Table 14.4. Operators, Tags and Metafunctions
表 14.4. 操作符,標籤和元函數

Operator 操作符

Proto Tag  Proto標籤

Proto Metafunction  Proto元函數

unary +

proto::tag::unary_plus

proto::unary_plus<>

unary -

proto::tag::negate

proto::negate<>

unary *

proto::tag::dereference

proto::dereference<>

unary ~

proto::tag::complement

proto::complement<>

unary &

proto::tag::address_of

proto::address_of<>

unary !

proto::tag::logical_not

proto::logical_not<>

unary prefix ++

proto::tag::pre_inc

proto::pre_inc<>

unary prefix --

proto::tag::pre_dec

proto::pre_dec<>

unary postfix ++

proto::tag::post_inc

proto::post_inc<>

unary postfix --

proto::tag::post_dec

proto::post_dec<>

binary <<

proto::tag::shift_left

proto::shift_left<>

binary >>

proto::tag::shift_right

proto::shift_right<>

binary *

proto::tag::multiplies

proto::multiplies<>

binary /

proto::tag::divides

proto::divides<>

binary %

proto::tag::modulus

proto::modulus<>

binary +

proto::tag::plus

proto::plus<>

binary -

proto::tag::minus

proto::minus<>

binary <

proto::tag::less

proto::less<>

binary >

proto::tag::greater

proto::greater<>

binary <=

proto::tag::less_equal

proto::less_equal<>

binary >=

proto::tag::greater_equal

proto::greater_equal<>

binary ==

proto::tag::equal_to

proto::equal_to<>

binary !=

proto::tag::not_equal_to

proto::not_equal_to<>

binary ||

proto::tag::logical_or

proto::logical_or<>

binary &&

proto::tag::logical_and

proto::logical_and<>

binary &

proto::tag::bitwise_and

proto::bitwise_and<>

binary |

proto::tag::bitwise_or

proto::bitwise_or<>

binary ^

proto::tag::bitwise_xor

proto::bitwise_xor<>

binary ,

proto::tag::comma

proto::comma<>

binary ->*

proto::tag::mem_ptr

proto::mem_ptr<>

binary =

proto::tag::assign

proto::assign<>

binary <<=

proto::tag::shift_left_assign

proto::shift_left_assign<>

binary >>=

proto::tag::shift_right_assign

proto::shift_right_assign<>

binary *=

proto::tag::multiplies_assign

proto::multiplies_assign<>

binary /=

proto::tag::divides_assign

proto::divides_assign<>

binary %=

proto::tag::modulus_assign

proto::modulus_assign<>

binary +=

proto::tag::plus_assign

proto::plus_assign<>

binary -=

proto::tag::minus_assign

proto::minus_assign<>

binary &=

proto::tag::bitwise_and_assign

proto::bitwise_and_assign<>

binary |=

proto::tag::bitwise_or_assign

proto::bitwise_or_assign<>

binary ^=

proto::tag::bitwise_xor_assign

proto::bitwise_xor_assign<>

binary subscript

proto::tag::subscript

proto::subscript<>

ternary ?:

proto::tag::if_else_

proto::if_else_<>

n-ary function call

proto::tag::function

proto::function<>


Expression trees can have a very rich and complicated structure. Often, you need to know some things about an expression's structure before you can process it. This section describes the tools Proto provides for peering inside an expression tree and discovering its structure. And as you'll see in later sections, all the really interesting things you can do with Proto begin right here.
表達式樹可以具有非常大而複雜的結構。通常,在你可以處理一個表達式結構之前,你需要瞭解關於它的一些事情。本節描述 Proto 所提供的一些工具,用於窺探表達式樹的內部和發現其結構。在後面的章節中你將看到,你可以用 Proto 所做的真正有趣的事情從這裡才開始。

Imagine your DSEL is a miniature I/O facility, with iostream operations that execute lazily. You might want expressions representing input operations to be processed by one function, and output operations to be processed by a different function. How would you do that?
想像你的DSEL是一個微型的I/O工具,可以延遲執行 iostream 操作。你可能想讓表示輸入操作的表達式可以被某個函數處理,而表示輸出操作的則被另一個函數處理。你要怎樣做呢?

The answer is to write patterns (a.k.a, grammars) that match the structure of input and output expressions. Proto provides utilities for defining the grammars, and the proto::matches<> template for checking whether a given expression type matches the grammar.
答案是編寫匹配輸入和輸出表達式結構的模式(也稱語法)。Proto 提供了定義語法的工具,以及用於檢查一個給定的表達式類型是否匹配該語法的 proto::matches<> 模板。

First, let's define some terminals we can use in our lazy I/O expressions:
首先,我們來定義一些要在我們的惰性I/O表達式中使用的終結符:

proto::terminal< std::istream & >::type cin_ = { std::cin };
proto::terminal< std::ostream & >::type cout_ = { std::cout };

Now, we can use cout_ instead of std::cout, and get I/O expression trees that we can execute later. To define grammars that match input and output expressions of the form cin_ >> i and cout_ << 1 we do this:
現在,我們可以用 cout_ 替代 std::cout,並得到可以延遲執行的I/O表達式樹。要定義匹配形如 cin_ >> icout_ << 1 的輸入輸出表達式的語法,我們可以這樣做:

struct Input
  : proto::shift_right< proto::terminal< std::istream & >, proto::_ >
{};

struct Output
  : proto::shift_left< proto::terminal< std::ostream & >, proto::_ >
{};

We've seen the template proto::terminal<> before, but here we're using it without accessing the nested ::type. When used like this, it is a very simple grammar, as are proto::shift_right<> and proto::shift_left<>. The newcomer here is _ in the proto namespace. It is a wildcard that matches anything. The Input struct is a grammar that matches any right-shift expression that has a std::istream terminal as its left operand.
之前我們已見過模板 proto::terminal<>,但是我們在這裡使用它並沒有訪問嵌套的 ::type。像這樣使用時,語法非常簡單,如 proto::shift_right<>proto::shift_left<>。這裡新出現的是位於 proto 名字空間中的 _。它是一個可以匹配任何東西的通配符。結構 Input 就是一個可以匹配任何以一個 std::istream 終結符作為左操作數的右移表達式的語法。

We can use these grammars together with the proto::matches<> template to query at compile time whether a given I/O expression type is an input or output operation. Consider the following:
我們可以將這些語法和 proto::matches<> 模板一起使用,在編譯期查詢一個給定的I/O表達式類型是否為輸入或輸出操作。考慮以下代碼:

template< typename Expr >
void input_output( Expr const & expr )
{
    if( proto::matches< Expr, Input >::value )
    {
        std::cout << "Input!\n";
    }

    if( proto::matches< Expr, Output >::value )
    {
        std::cout << "Output!\n";
    }
}

int main()
{
    int i = 0;
    input_output( cout_ << 1 );
    input_output( cin_ >> i );

    return 0;
}

This program prints the following:
該程序輸出如下:

Output!
Input!

If we wanted to break the input_output() function into two functions, one that handles input expressions and one for output expressions, we can use boost::enable_if<>, as follows:
如果我們想將 input_output() 函數分開為兩個函數,一個處理輸入表達式,另一個處理輸出表達式,我們可以用 boost::enable_if<>,如下:

template< typename Expr >
typename boost::enable_if< proto::matches< Expr, Input > >::type
input_output( Expr const & expr )
{
    std::cout << "Input!\n";
}

template< typename Expr >
typename boost::enable_if< proto::matches< Expr, Output > >::type
input_output( Expr const & expr )
{
    std::cout << "Output!\n";
}

This works as the previous version did. However, the following does not compile at all:
這一個和前一個版本結果一樣。不過,以下代碼就不能通過編譯:

input_output( cout_ << 1 << 2 ); // oops!

What's wrong? The problem is that this expression does not match our grammar. The expression groups as if it were written like (cout_ << 1) << 2. It will not match the Output grammar, which expects the left operand to be a terminal, not another left-shift operation. We need to fix the grammar.
有什麼錯嗎?問題在於,這個表達式不能匹配我們的語法。這個表達式組和以下寫法一樣:(cout_ << 1) << 2。它不能匹配 Output 語法,後者要求左操作數是一個終結符,而不是另一個左移操作。我們要修正這個語法。

We notice that in order to verify an expression as input or output, we'll need to recurse down to the bottom-left-most leaf and check that it is a std::istream or std::ostream. When we get to the terminal, we must stop recursing. We can express this in our grammar using proto::or_<>. Here are the correct Input and Output grammars:
我們注意到,為了檢驗一個表達式是輸入還是輸出,我們需要向下遞歸至最左下方的葉子並檢查它是 std::istream 還是 std::ostream。當我們得到終結符時,我們必須停止遞歸。我們可以在我們的語法中用 proto::or_<> 來表示這一點。以下是正確的 InputOutput 語法:

struct Input
  : proto::or_<
        proto::shift_right< proto::terminal< std::istream & >, proto::_ >
      , proto::shift_right< Input, proto::_ >
    >
{};

struct Output
  : proto::or_<
        proto::shift_left< proto::terminal< std::ostream & >, proto::_ >
      , proto::shift_left< Output, proto::_ >
    >
{};

This may look a little odd at first. We seem to be defining the Input and Output types in terms of themselves. This is perfectly OK, actually. At the point in the grammar that the Input and Output types are being used, they are incomplete, but by the time we actually evaluate the grammar with proto::matches<>, the types will be complete. These are recursive grammars, and rightly so because they must match a recursive data structure!
乍一看,這有點怪。看起來,我們是在用 InputOutput 類型來定義它們本身。事實上這是完全可以的。在這個語法中,到使用 InputOutput 類型的地方為止,它們還是不完整的,但是到我們使用 proto::matches<> 對該語法進行求值時,這兩個類型已經是完整的了。這是遞歸語法,也是正確的,因為它們必須匹配一個遞歸的數據結構!

When the Output grammar is evaluated against an expression like cout_ << 1 << 2, the first alternate of the proto::or_<> is tried first. It will fail, because the expression cout_ << 1 << 2 does not match the grammar proto::shift_left< proto::terminal< std::ostream & >, proto::_ >. Then the second alternate is tried. We match the expression against proto::shift_left< Output, proto::_ >. The expression is a left-shift, so we try the operands. The right operand 2 matches proto::_ trivially. To see if the left operand cout_ << 1 matches Output, we must recursively evaluate the Output grammar. This time we succeed, because cout_ << 1 will match the first alternate of the proto::or_<>. We're done -- the grammar matches successfully.
當這個 Output 語法針對一個形如 cout_ << 1 << 2 的表達式進行求值時,首先嘗試 proto::or_<> 的第一個選擇。這會失敗,因為表達式 cout_ << 1 << 2 不能匹配語法 proto::shift_left< proto::terminal< std::ostream & >, proto::_ >。然後嘗試第二個選擇。我們用 proto::shift_left< Output, proto::_ > 來匹配這個表達式。這個表達式是一個左移操作,所以我們先嘗試操作數。右操作數 2 可以匹配 proto::_。現在看左操作數 cout_ << 1 是否能匹配 Output,我們必須遞歸求值 Output 語法。這一次我們成功了,因為 cout_ << 1 將匹配 proto::or_<> 的第一個選擇。匹配完成 -- 這個語法可以成功地匹配。

The terminals in an expression tree could be const or non-const references, or they might not be references at all. When writing grammars, you usually don't have to worry about it because proto::matches<> gives you a little wiggle room when matching terminals. A grammar such as proto::terminal<int> will match a terminal of type int, int &, or int const &.
一棵表達式樹中的終結符可以是const的或非const的引用,或者根本不是引用。在編寫語法時,你通常不需要擔心這一點,因為 proto::matches<> 在匹配終結符時給了你一點寬鬆的空間。一個形如 proto::terminal<int> 的語法可以匹配類型為 int, int &, 或 int const & 的終結符。

You can explicitly specify that you want to match a reference type. If you do, the type must match exactly. For instance, a grammar such as proto::terminal<int &> will only match an int &. It will not match an int or an int const &.
你可以明確指定你要匹配的引用類型。如果你這樣做了,類型將必須精確匹配。例如,一個形如 proto::terminal<int &> 的語法只能匹配一個 int &。它不能匹配 intint const &

The table below shows how Proto matches terminals. The simple rule is: if you want to match only reference types, you must specify the reference in your grammar. Otherwise, leave it off and Proto will ignore const and references.
下表展示了 Proto 是如何匹配終結符的。簡單的規則是:如果你想只匹配引用類型,你必須在你的語法中指定引用。否則,不用指定它,而 Proto 將忽略 const 和引用。

Table 14.5. proto::matches<> and Reference / CV-Qualification of Terminals
Table 14.5. proto::matches<> 和終結符的引用/CV限定

Terminal 終結符

Grammar 語法

Matches? 是否匹配?

T

T

yes

T &

T

yes

T const &

T

yes

T

T &

no

T &

T &

yes

T const &

T &

no

T

T const &

no

T &

T const &

no

T const &

T const &

yes


This begs the question: What if you want to match an int, but not an int & or an int const &? For forcing exact matches, Proto provides the proto::exact<> template. For instance, proto::terminal< proto::exact<int> > would only match an int held by value.
這裡迴避了一個問題:如果你想匹配一個 int,而不匹配 int &int const &,該怎麼辦?為了強制精確的匹配,Proto 提供了 proto::exact<> 模板。例如,proto::terminal< proto::exact<int> > 將只匹配一個以值方式保存的 int

Proto gives you extra wiggle room when matching array types. Array types match themselves or the pointer types they decay to. This is especially useful with character arrays. The type returned by proto::as_expr("hello") is proto::terminal<char const[6]>::type. That's a terminal containing a 6-element character array. Naturally, you can match this terminal with the grammar proto::terminal<char const[6]>, but the grammar proto::terminal<char const *> will match it as well, as the following code fragment illustrates.
在匹配數組類型時,Proto 為你提供了更為寬鬆的空間。數組類型可以匹配它們本身或它們退化的指針類型。這對於字符數組尤其有用。由 proto::as_expr("hello") 所返回的類型為 proto::terminal<char const[6]>::type。這是一個包含6個元素的字符數組的終結符。自然,你可以用語法 proto::terminal<char const[6]> 來匹配這個終結符,不過語法 proto::terminal<char const *> 也可以匹配它,如以下代碼片斷所示。

struct CharString
  : proto::terminal< char const * >
{};

typedef proto::terminal< char const[6] >::type char_array;

BOOST_MPL_ASSERT(( proto::matches< char_array, CharString > ));

What if we only wanted CharString to match terminals of exactly the type char const *? You can use proto::exact<> here to turn off the fuzzy matching of terminals, as follows:
如果我們想 CharString 只精確匹配類型為 char const * 的終結符,該如何?你可以用 proto::exact<> 來關閉對終結符的模糊匹配,如下:

struct CharString
  : proto::terminal< proto::exact< char const * > >
{};

typedef proto::terminal<char const[6]>::type char_array;
typedef proto::terminal<char const *>::type  char_string;

BOOST_MPL_ASSERT(( proto::matches< char_string, CharString > ));
BOOST_MPL_ASSERT_NOT(( proto::matches< char_array, CharString > ));

Now, CharString does not match array types, only character string pointers.
現在,CharString 將不能匹配數組類型,只能匹配字符串指針。

The inverse problem is a little trickier: what if you wanted to match all character arrays, but not character pointers? As mentioned above, the expression as_expr("hello") has the type proto::terminal< char const[ 6 ] >::type. If you wanted to match character arrays of arbitrary size, you could use proto::N, which is an array-size wildcard. The following grammar would match any string literal: proto::terminal< char const[ proto::N ] >.
相反的問題則有點微妙:如果你想匹配所有字符數組而不想匹配字符指針呢?如上所述,表達式 as_expr("hello") 具有 proto::terminal< char const[ 6 ] >::type 的類型。如果你想匹配任意大小的字符數組,你可以用 proto::N,它是一個數組大小通配符。以下語法將匹配任何字符串字面值:proto::terminal< char const[ proto::N ] >

Sometimes you need even more wiggle room when matching terminals. For example, maybe you're building a calculator DSEL and you want to allow any terminals that are convertible to double. For that, Proto provides the proto::convertible_to<> template. You can use it as: proto::terminal< proto::convertible_to< double > >.
有時候,在匹配終結符時你需要更多的空間。例如,也許你正在構建一個計算器DSEL,你想允許任何可以轉換為 double 的終結符。為此,Proto 提供了 proto::convertible_to<> 模板。你可以這樣來使用它:proto::terminal< proto::convertible_to< double > >

There is one more way you can perform a fuzzy match on terminals. Consider the problem of trying to match a std::complex<> terminal. You can easily match a std::complex<float> or a std::complex<double>, but how would you match any instantiation of std::complex<>? You can use proto::_ here to solve this problem. Here is the grammar to match any std::complex<> instantiation:
你還有一個方法可以對終結符執行模糊匹配。考慮這樣一個問題,你嘗試匹配一個 std::complex<> 終結符。你可以很容易地匹配一個 std::complex<float>std::complex<double>,但是你如何匹配任意的 std::complex<> 實例呢?你可以用 proto::_ 來解決這個問題。以下是匹配任意的 std::complex<> 實例:

struct StdComplex
  : proto::terminal< std::complex< proto::_ > >
{};

When given a grammar like this, Proto will deconstruct the grammar and the terminal it is being matched against and see if it can match all the constituents.
當給出這樣一個語法時,Proto 將解構要匹配的語法和終結符,看看它是否可以匹配所有要素。

We've already seen how to use expression generators like proto::terminal<> and proto::shift_right<> as grammars. We've also seen proto::or_<>, which we can use to express a set of alternate grammars. There are a few others of interest; in particular, proto::if_<>, proto::and_<> and proto::not_<>.
我們已經看到如何把象 proto::terminal<>proto::shift_right<> 這樣的表達式生成器作為語法使用。我們也看到了 proto::or_<>,我們可以用它來表達一組N選1的語法。還有另外幾個有趣的模板:proto::if_<>, proto::and_<>proto::not_<>

The proto::not_<> template is the simplest. It takes a grammar as a template parameter and logically negates it; not_<Grammar> will match any expression that Grammar does not match.
proto::not_<> 模板最為簡單。它接受一個語法作為模板參數,並在邏輯上否定它;not_<Grammar> 將匹配 Grammar 不能匹配的任意表達式。

The proto::if_<> template is used together with a Proto transform that is evaluated against expression types to find matches. (Proto transforms will be described later.)
proto::if_<> 模板與一個 Proto 變換一起使用,該變換對表達式類型進行求值以發現匹配(Proto 變換將在稍後討論)。

The proto::and_<> template is like proto::or_<>, except that each argument of the proto::and_<> must match in order for the proto::and_<> to match. As an example, consider the definition of CharString above that uses proto::exact<>. It could have been written without proto::exact<> as follows: 
proto::and_<> 模板類似於 proto::or_<>,不過 proto::and_<> 的每個參數必須都匹配,proto::and_<> 才能匹配。例如,考慮前面那個使用 proto::exact<>CharString 定義。它可以不用 proto::exact<>,改寫為:

struct CharString
  : proto::and_<
        proto::terminal< proto::_ >
      , proto::if_< boost::is_same< proto::_value, char const * >() >
    >
{};

This says that a CharString must be a terminal, and its value type must be the same as char const *. Notice the template argument of proto::if_<>: boost::is_same< proto::_value, char const * >(). This is Proto transform that compares the value type of a terminal to char const *.
這是說,一個 CharString 必須是一個終結符,它的值類型必須與 char const * 相同。注意,proto::if_<> 的模板參數:boost::is_same< proto::_value, char const * >()。這是一個 Proto 變換,它將一個終結符的值類型與 char const * 進行比較。

The proto::if_<> template has a couple of variants. In addition to if_<Condition> you can also say if_<Condition, ThenGrammar> and if_<Condition, ThenGrammar, ElseGrammar>. These let you select one sub-grammar or another based on the Condition.
proto::if_<> 模板有幾個變體。除了 if_<Condition>,你還可以用 if_<Condition, ThenGrammar>if_<Condition, ThenGrammar, ElseGrammar>。它們讓你基於 Condition 選擇某個子語法或另一個子語法。

When your Proto grammar gets large, you'll start to run into some scalability problems with proto::or_<>, the construct you use to specify alternate sub-grammars. First, due to limitations in C++, proto::or_<> can only accept up to a certain number of sub-grammars, controlled by the BOOST_PROTO_MAX_LOGICAL_ARITY macro. This macro defaults to eight, and you can set it higher, but doing so will aggravate another scalability problem: long compile times. With proto::or_<>, alternate sub-grammars are tried in order -- like a series of cascading if's -- leading to lots of unnecessary template instantiations. What you would prefer instead is something like switch that avoids the expense of cascading if's. That's the purpose of proto::switch_<>; although less convenient than proto::or_<>, it improves compile times for larger grammars and does not have an arbitrary fixed limit on the number of sub-grammars.
當你的 Proto 語法變大時,你將會開始陷入到 proto::or_<> 所帶來的一些可擴展性問題,它是你用於指定可選子語法的結構。首先,由於C++的限制,proto::or_<> 最多只能接受某個特定數量的子語法,這個數量由 BOOST_PROTO_MAX_LOGICAL_ARITY 宏控制。這個宏缺省為8,你可以設高一點,但是這樣做會加劇另一個可擴展性問題:更長的編譯時間。使用 proto::or_<> 時,侯選的子語法是按順序嘗試的 -- 就像一系列層疊的 if -- 導致了大量無用的模板實例化。你應該用一些類似於 switch 的東西來避免這些層疊 if 的代價。這正是 proto::switch_<> 的目的;雖然它不如 proto::or_<> 方便,但是它在使用大量語法時改進了編譯的時間,而且對於子語法的數量也沒有一個固定的限制。

Let's illustrate how to use proto::switch_<> by first writing a big grammar with proto::or_<> and then translating it to an equivalent grammar using proto::switch_<>:
我們來示範一下如何使用 proto::switch_<>,首先寫用 proto::or_<> 寫一個大語法,然後將它翻譯為使用 proto::switch_<> 等價語法:

// Here is a big, inefficient grammar 以下是一個大的、低效的語法
struct ABigGrammar : proto::or_< proto::terminal<int> , proto::terminal<double> , proto::unary_plus<ABigGrammar> , proto::negate<ABigGrammar> , proto::complement<ABigGrammar> , proto::plus<ABigGrammar, ABigGrammar> , proto::minus<ABigGrammar, ABigGrammar> , proto::or_< proto::multiplies<ABigGrammar, ABigGrammar> , proto::divides<ABigGrammar, ABigGrammar> , proto::modulus<ABigGrammar, ABigGrammar> > > {};

The above might be the grammar to a more elaborate calculator DSEL. Notice that since there are more than eight sub-grammars, we had to chain the sub-grammars with a nested proto::or_<> -- not very nice.
以上是為一個更為精細的計算器DSEL所編寫的語法。注意,由於超過了8個子語法,所以我們必須用嵌套的 proto::or_<> 來串起這些子語法 -- 不夠漂亮。

The idea behind proto::switch_<> is to dispatch based on an expression's tag type to a sub-grammar that handles expressions of that type. To use proto::switch_<>, you define a struct with a nested case_<> template, specialized on tag types. The above grammar can be expressed using proto::switch_<> as follows. It is described below.
在 proto::switch_<> 背後的想法是,基於一個表達式的標籤類型來分派到處理該類型的表達式的子語法。要使用 proto::switch_<>,你要定義一個結構,該結構要有一個以標籤類型作為參數的嵌套 case_<> 模板。上述語法可以用 proto::switch_<> 表示如下。後面我們再討論它。

// Redefine ABigGrammar more efficiently using proto::switch_<>
// 用 proto::switch_<> 重新定義一個更高效的 ABigGrammar
struct ABigGrammar; struct ABigGrammarCases { // The primary template matches nothing: 主模板不匹配東西:
template<typename Tag> struct case_ : proto::not_<_> {}; }; // Terminal expressions are handled here 終結符表達式在此處理
template<> struct ABigGrammarCases::case_<proto::tag::terminal> : proto::or_< proto::terminal<int> , proto::terminal<double> > {}; // Non-terminals are handled similarly 非終結符類似地處理
template<> struct ABigGrammarCases::case_<proto::tag::unary_plus> : proto::unary_plus<ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::negate> : proto::negate<ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::complement> : proto::complement<ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::plus> : proto::plus<ABigGrammar, ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::minus> : proto::minus<ABigGrammar, ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::multiplies> : proto::multiplies<ABigGrammar, ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::divides> : proto::divides<ABigGrammar, ABigGrammar> {}; template<> struct ABigGrammarCases::case_<proto::tag::modulus> : proto::modulus<ABigGrammar, ABigGrammar> {}; // Define ABigGrammar in terms of ABigGrammarCases
// using proto::switch_<>
// 用 proto::switch_<> 根據 ABigGrammarCases 定義 ABigGrammar
struct ABigGrammar : proto::switch_<ABigGrammarCases> {};

Matching an expression type E against proto::switch_<C> is equivalent to matching it against C::case_<E::proto_tag>. By dispatching on the expression's tag type, we can jump to the sub-grammar that handles expressions of that type, skipping over all the other sub-grammars that couldn't possibly match. If there is no specialization of case_<> for a particular tag type, we select the primary template. In this case, the primary template inherits from proto::not_<_> which matches no expressions.
proto::switch_<C> 匹配某個表達式類型 E,相當於用 C::case_<E::proto_tag> 來匹配它。通過按表達式的標籤類型來分派,我們可以跳至處理該類型的表達式的子語法,跳過其它所有不可能匹配的子語法。如果某個特定標籤類型沒有相應的 case_<> 特化,則我們選擇主模板。在這個例子中,主模板派生自 proto::not_<_>,它不匹配任何表達式。

Notice the specialization that handles terminals:
留意處理終結符的特化:

// Terminal expressions are handled here 終結符表達式在此處理
template<> struct ABigGrammarCases::case_<proto::tag::terminal> : proto::or_< proto::terminal<int> , proto::terminal<double> > {};

The proto::tag::terminal type by itself isn't enough to select an appropriate sub-grammar, so we use proto::or_<> to list the alternate sub-grammars that match terminals. 
類型 proto::tag::terminal 本身不足以選擇一個合適的子語法,所以我們用 proto::or_<> 來列出匹配終結符的候選子語法。

[Note] Note 備註

You might be tempted to define your case_<> specializations in situ as follows:
你可能會像下面這樣在原地定義你的 case_<> 特化:

struct ABigGrammarCases
{
    template<typename Tag>
    struct case_ : proto::not_<_> {};

    // ERROR: not legal C++ 錯誤:不是合法的C++
template<> struct case_<proto::tag::terminal> /* ... */ };

Unfortunately, for arcane reasons, it is not legal to define an explicit nested specialization in situ like this. It is, however, perfectly legal to define partial specializations in situ, so you can add a extra dummy template parameter that has a default, as follows:
不幸的是,由於某些原因,像這樣在原地定義一個顯式的嵌套特化是不合法的。但是,在原地定義偏特化則是合法的,所以你可以像下面這樣增加一個有缺省值的啞模板參數:

struct ABigGrammarCases
{
    // Note extra "Dummy" template parameter here: 
// 注意這裡加了一個"啞"的模板參數:
template<typename Tag, int Dummy = 0> struct case_ : proto::not_<_> {}; // OK: "Dummy" makes this a partial specialization
// instead of an explicit specialization.
// 可以:"啞"參數使得這是一個偏特化而不是顯式特化。
template<int Dummy> struct case_<proto::tag::terminal, Dummy> /* ... */ };

You might find this cleaner than defining explicit case_<> specializations outside of their enclosing struct.
也許你會發現這種方法比在結構外部定義顯式的 case_<> 特化更為清晰。

Not all of C++'s overloadable operators are unary or binary. There is the oddball operator() -- the function call operator -- which can have any number of arguments. Likewise, with Proto you may define your own "operators" that could also take more that two arguments. As a result, there may be nodes in your Proto expression tree that have an arbitrary number of children (up to BOOST_PROTO_MAX_ARITY, which is configurable). How do you write a grammar to match such a node?
並非所有的C++可重載操作符都是單參數或雙參數的。有一個奇特的 operator() -- 函數調用操作符 -- 它可以帶有任意數量的參數。同樣,在 Proto 中你可以定義你自己的"操作符"來接受兩個以上的參數。因此,在你的 Proto 表達式樹中可能有某些節點帶有任意數量的子節點(最多為 BOOST_PROTO_MAX_ARITY 個,這是可配置的)。你如何編寫一個可以匹配此類節點的語法呢?

For such cases, Proto provides the proto::vararg<> class template. Its template argument is a grammar, and the proto::vararg<> will match the grammar zero or more times. Consider a Proto lazy function called fun() that can take zero or more characters as arguments, as follows:
對於這種情況,Proto 提供了 proto::vararg<> 類模板。它的模板參數是一個語法,而且 proto::vararg<> 可以零次或多次匹配該語法。考慮一個名為 fun() 的 Proto 惰性函數,它可以接受零個或多個字符作為參數,如下:

struct fun_tag {};
struct FunTag : proto::terminal< fun_tag > {};
FunTag::type const fun = {{}};

// example usage: 用例:
fun(); fun('a'); fun('a', 'b'); ...

Below is the grammar that matches all the allowable invocations of fun():
以下是可以匹配所有可允許的 fun() 調用的語法:

struct FunCall
  : proto::function< FunTag, proto::vararg< proto::terminal< char > > >
{};

The FunCall grammar uses proto::vararg<> to match zero or more character literals as arguments of the fun() function.
語法 FunCall 使用 proto::vararg<> 來匹配零個或多個作為 fun() 函數的參數的字符字面值。

As another example, can you guess what the following grammar matches?
作為另一個例子,你能猜出以下語法匹配什麼嗎?

struct Foo
  : proto::or_<
        proto::terminal< proto::_ >
      , proto::nary_expr< proto::_, proto::vararg< Foo > >
    >
{};

Here's a hint: the first template parameter to proto::nary_expr<> represents the node type, and any additional template parameters represent child nodes. The answer is that this is a degenerate grammar that matches every possible expression tree, from root to leaves.
有一個提示:proto::nary_expr<> 的第一個模板參數表示節點的類型,其它參數則表示子節點。答案是,這是一個退化的語法,匹配任意一個可能的表達式樹,從根到葉子。

In this section we'll see how to use Proto to define a grammar for your DSEL and use it to validate expression templates, giving short, readable compile-time errors for invalid expressions.
在本節中,我們將看到如何使用 Proto 來為你的DSEL定義一個語法,並用它來驗證表達式模板,為無效的表達式給出簡短可讀的編譯期錯誤。

[Tip] Tip 提示

You might think that this is a backwards way of doing things. 「If Proto let me select which operators to overload, my users wouldn't be able to create invalid expressions in the first place, and I wouldn't need a grammar at all!」 That may be true, but there are reasons for preferring to do things this way.
你可能會認為這是一個倒退的處理方式。「如果 Proto 讓我選擇哪個重載哪個操作符,那麼我的用戶一開始就不能創建無效的表達式,我也就根本不需要什麼語法了!」 這也許是對的,不過也有其它理由要這樣做。

First, it lets you develop your DSEL rapidly -- all the operators are there for you already! -- and worry about invalid syntax later.
首先,它可以讓你快速地開發你的DSEL -- 所有操作符都已經為你準備好了! -- 只需要關心無效的語法就可以了。

Second, it might be the case that some operators are only allowed in certain contexts within your DSEL. This is easy to express with a grammar, and hard to do with straight operator overloading.
其次,有可能在你的DSEL中有些操作符只在特定的上下文中可用。這一點用語法可以很容易表達,而直接用操作符重載則很難。

Third, using a DSEL grammar to flag invalid expressions can often yield better errors than manually selecting the overloaded operators.
第三,用DSEL語法來標示無效表達式通常可以產生比手工選擇操作符重載更好的錯誤提示。

Fourth, the grammar can be used for more than just validation. You can use your grammar to define tree transformations that convert expression templates into other more useful objects.
第四,語法可以不僅用於驗證。你可以用你的語法來定義樹變換,將表達式模板轉換為更為有用的對象。

If none of the above convinces you, you actually can use Proto to control which operators are overloaded within your domain. And to do it, you need to define a grammar!
如果以上理由都不能說服你,那麼實際上你可以用 Proto 來控制在你的領域中重載哪些操作符。而要實現這一點,你需要定義一個語法!

In a previous section, we used Proto to define a DSEL for a lazily evaluated calculator that allowed any combination of placeholders, floating-point literals, addition, subtraction, multiplication, division and grouping. If we were to write the grammar for this DSEL in EBNF, it might look like this:
在上一節中,我們用 Proto 為一個惰性求值計算器下定義了一個DSEL,這個DSEL允許佔位符、浮點字面值、加法、減法、乘法、除法和分組的任意組合。如果我們要用 EBNF 為這個DSEL寫一個語法,它看起來會是這樣:

group       ::= '(' expression ')'
factor ::= double | '_1' | '_2' | group
term ::= factor (('*' factor) | ('/' factor))*
expression ::= term (('+' term) | ('-' term))*

This captures the syntax, associativity and precedence rules of a calculator. Writing the grammar for our calculator DSEL using Proto is even simpler. Since we are using C++ as the host language, we are bound to the associativity and precedence rules for the C++ operators. Our grammar can assume them. Also, in C++ grouping is already handled for us with the use of parenthesis, so we don't have to code that into our grammar.
這裡包括了一個計算器的語法、結合律和優先級。用 Proto 編寫我們的計算器DSEL語法更加簡單。因為我們以C++為宿主語言,所以我們綁定了C++操作符的結合律和優先級。我們的語法可以以此為前提。此外,在C++中,使用括號進行分組也已經為我們準備好了,因此我們不需要為這些在我們的語法中編寫代碼。

Let's begin our grammar for forward-declaring it:
我們從語法的前向聲明開始:

struct CalculatorGrammar;

It's an incomplete type at this point, but we'll still be able to use it to define the rules of our grammar. Let's define grammar rules for the terminals:
此時,它是一個不完整的類型,不過我們還是可以用它來定義我們的語法規則。讓我們來定義終結符的語法規則:

struct Double
  : proto::terminal< proto::convertible_to< double > >
{};

struct Placeholder1
  : proto::terminal< placeholder<0> >
{};

struct Placeholder2
  : proto::terminal< placeholder<1> >
{};

struct Terminal
  : proto::or_< Double, Placeholder1, Placeholder2 >
{};

Now let's define the rules for addition, subtraction, multiplication and division. Here, we can ignore issues of associativity and precedence -- the C++ compiler will enforce that for us. We only must enforce that the arguments to the operators must themselves conform to the CalculatorGrammar that we forward-declared above.
現在我們來定義加法、減法、乘法和除法的規則。這裡,我們可以忽略結合律和優先級的問題 -- C++編譯器會為我們強制執行它。我們只需規定操作符的參數必須符合我們前面所聲明的 CalculatorGrammar 就可以了。

struct Plus
  : proto::plus< CalculatorGrammar, CalculatorGrammar >
{};

struct Minus
  : proto::minus< CalculatorGrammar, CalculatorGrammar >
{};

struct Multiplies
  : proto::multiplies< CalculatorGrammar, CalculatorGrammar >
{};

struct Divides
  : proto::divides< CalculatorGrammar, CalculatorGrammar >
{};

Now that we've defined all the parts of the grammar, we can define CalculatorGrammar:
現在我們已經定義了這個語法的全部,我們可以定義 CalculatorGrammar 了:

struct CalculatorGrammar
  : proto::or_<
        Terminal
      , Plus
      , Minus
      , Multiplies
      , Divides
    >
{};

That's it! Now we can use CalculatorGrammar to enforce that an expression template conforms to our grammar. We can use proto::matches<> and BOOST_MPL_ASSERT() to issue readable compile-time errors for invalid expressions, as below:
這就是它了!現在我們可以用 CalculatorGrammar 來強制某個表達式模板符合我們的語法。我們可以用 proto::matches<>BOOST_MPL_ASSERT() 來對無效表達式生成可讀的編譯期錯誤,如下:

template< typename Expr >
void evaluate( Expr const & expr )
{
    BOOST_MPL_ASSERT(( proto::matches< Expr, CalculatorGrammar > ));
    // ...
}

Now that you've written the front end for your DSEL compiler, and you've learned a bit about the intermediate form it produces, it's time to think about what to do with the intermediate form. This is where you put your domain-specific algorithms and optimizations. Proto gives you two ways to evaluate and manipulate expression templates: contexts and transforms.
現在你已經為你的DSEL編寫了前端,也已經學習了一些關於它所生成的中間格式的知識,是時候考慮要對這個中間格式做些什麼了。這就是你置入你的領域專用算法和優化的地方。Proto 為你提供了兩種方法來對表達式模板進行求值和處理:上下文和變換。

  • A context is like a function object that you pass along with an expression to the proto::eval() function. It associates behaviors with node types. proto::eval() walks the expression and invokes your context at each node.
    上下文context 類似於一個函數對象,你可以把它與某個表達式一起傳遞給 proto::eval() 函數。它將某些行為與節點類型關聯起來。proto::eval() 對表達式進行遍歷並在每個節點處調用你的上下文。
  • A transform is a way to associate behaviors, not with node types in an expression, but with rules in a Proto grammar. In this way, they are like semantic actions in other compiler-construction toolkits.
    變換transform 是將某些行為與 Proto 語法中的規則而不是與表達式的節點類型相關聯的方法。使用這種方法,類似於其它編譯器構造工具中的語義動作。

Two ways to evaluate expressions! How to choose? Contexts are a bit simpler to understand and to debug, since they are largely procedural, so contexts are a good place to start. But although transforms are more advanced, they are also more powerful; since they are associated with rules in your grammar, you can select the proper transform based on the entire structure of a sub-expression rather than simply on the type of its top-most node.
有兩種方法對表達式進行求值!如何選擇?上下文對於理解和調試來說更簡單些,因為它基本上是過程性的,所以上下文是一個良好的開端。但是,變換更為高級,也更強大;因為它是與你的語法中的規則相關聯的,你可以基於某個子表達式的整個結構而不僅僅是頂層節點的類型來選擇正確的變換。

Also, transforms have a concise and declarative syntax that can be confusing at first, but highly expressive and fungible once you become accustomed to it. And -- this is admittedly very subjective -- the author finds programming with Proto transforms to be an inordinate amount of fun! Your mileage may vary.
另外,變換具有簡明和聲明性的語法,剛開始可能會有些混亂,但一旦你熟悉了它,就會發現它具有高度的表達力和可替代性。而且 -- 這一點公認是非常主觀的 -- 作者發現,用 Proto 變換來編程具有更多的樂趣!你的感覺可能會有所不同。

Once you have constructed a Proto expression tree, either by using Proto's operator overloads or with proto::make_expr() and friends, you probably want to actually do something with it. The simplest option is to use proto::eval(), a generic expression evaluator. To use proto::eval(), you'll need to define a context that tells proto::eval() how each node should be evaluated. This section goes through the nuts and bolts of using proto::eval(), defining evaluation contexts, and using the contexts that Proto provides.
一旦你用 Proto 的操作符重載或是用 proto::make_expr() 及其輔助物構造了一個 Proto 表達式樹,你可能想要實實在在地用它來做些什麼。最簡單的方式是使用 proto::eval(),它是一個泛型的表達式求值器。要使用 proto::eval(),你需要定義一個上下文,告知 proto::eval() 如何對每個節點進行求值。本節將介紹使用 proto::eval(),定義求值上下文,以及使用 Proto 所提供的上下文等細節。

[Note] Note 備註

proto::eval() is a less powerful but easier-to-use evaluation technique than Proto transforms, which are covered later. Although very powerful, transforms have a steep learning curve and can be more difficult to debug. proto::eval() is a rather weak tree traversal algorithm. Dan Marsden has been working on a more general and powerful tree traversal library. When it is ready, I anticipate that it will eliminate the need for proto::eval().
proto::eval() 是比 Proto 變換稍弱但更易於使用的一種求值技術,稍後我們將會討論到 Proto 變換。變換雖然很強大,但是卻具有陡峭的學習曲線,而且很難調試。proto::eval() 則是一種相對較弱的樹遍歷算法。Dan Marsden 已經在開發一個更為通用和強大的樹遍歷程序庫。當他完成時,我期望它可以完全替代 proto::eval()

Synopsis: 
摘要:

namespace proto
{
    namespace result_of
    {
        // A metafunction for calculating the return
// type of proto::eval() given certain Expr
// and Context types.
// 用於計算給定了Expr和Context類型的proto::eval()的返回類型的元函數
template<typename Expr, typename Context> struct eval { typedef typename Context::template eval<Expr>::result_type type; }; } namespace functional { // A callable function object type for evaluating
// a Proto expression with a certain context.
// 用於以特定上下文對一個Proto表達式進行求值的可調用函數對像
struct eval : callable { template<typename Sig> struct result; template<typename Expr, typename Context> typename proto::result_of::eval<Expr, Context>::type operator ()(Expr &expr, Context &context) const; template<typename Expr, typename Context> typename proto::result_of::eval<Expr, Context>::type operator ()(Expr &expr, Context const &context) const; }; } template<typename Expr, typename Context> typename proto::result_of::eval<Expr, Context>::type eval(Expr &expr, Context &context); template<typename Expr, typename Context> typename proto::result_of::eval<Expr, Context>::type eval(Expr &expr, Context const &context); }

Given an expression and an evaluation context, using proto::eval() is quite simple. Simply pass the expression and the context to proto::eval() and it does the rest and returns the result. You can use the eval<> metafunction in the proto::result_of namespace to compute the return type of proto::eval(). The following demonstrates a use of proto::eval():
給定一個表達式和一個求值上下文,使用 proto::eval() 是很簡單的。只要將表達式和上下文傳遞給 proto::eval() 就可以了,它會完成剩餘的任務並返回結果。你可以用 proto::result_of 名字空間中的 eval<> 元函數來計算 proto::eval() 的返回類型。以下示範了 proto::eval() 的使用:

template<typename Expr>
typename proto::result_of::eval<Expr const, MyContext>::type
MyEvaluate(Expr const &expr)
{
    // Some user-defined context type 用戶定義的上下文類型
MyContext ctx; // Evaluate an expression with the context 以該上下文對一個表達式求值
return proto::eval(expr, ctx); }

What proto::eval() does is also very simple. It defers most of the work to the context itself. Here essentially is the implementation of proto::eval():
proto::eval() 所做的也很簡單。它將多數工作推給上下文本身。以下是 proto::eval() 的實現:

// eval() dispatches to a nested "eval<>" function
// object within the Context:
// eval()分派至Context內嵌的一個"eval<>"函數對像:
template<typename Expr, typename Context> typename Context::template eval<Expr>::result_type eval(Expr &expr, Context &ctx) { typename Context::template eval<Expr> eval_fun; return eval_fun(expr, ctx); }

Really, proto::eval() is nothing more than a thin wrapper that dispatches to the appropriate handler within the context class. In the next section, we'll see how to implement a context class from scratch.
其實,proto::eval() 只是一個很薄的包裝器,將任務分派至上下文類中適當的處理者。在下一節中,我們將看到如何從零開始實現一個上下文類。

As we saw in the previous section, there is really not much to the proto::eval() function. Rather, all the interesting expression evaluation goes on within a context class. This section shows how to implement one from scratch.
正如我們在上一節中所看到的,對於 proto::eval() 函數確實沒有更多東西了。相反,表達式求值的所有有趣動作都是在上下文類的內部。本節將展示如何如何從零開始實現一個上下文。

All context classes have roughly the following form:
所有的上下文類都大概是以下形式:

// A prototypical user-defined context. 一個proto型的用戶自定義上下文。
struct MyContext { // A nested eval<> class template 嵌套的eval<>類模板
template< typename Expr , typename Tag = typename proto::tag_of<Expr>::type > struct eval; // Handle terminal nodes here... 在此處理終結符節點...
template<typename Expr> struct eval<Expr, proto::tag::terminal> { // Must have a nested result_type typedef. 必須有一個嵌套的result_type typedef.
typedef ... result_type; // Must have a function call operator that takes
// an expression and the context.
// 必須有一個函數調用操作符,接受一個表達式及本上下文。
result_type operator()(Expr &expr, MyContext &ctx) const { return ...; } }; // ... other specializations of struct eval<> ... ...其它的struct eval<>特化...
};

Context classes are nothing more than a collection of specializations of a nested eval<> class template. Each specialization handles a different expression type.
上下文類其實就是一組嵌套的 eval<> 類型模板的特化。每一個特化處理一種不同的表達式類型。

In the Hello Calculator section, we saw an example of a user-defined context class for evaluating calculator expressions. That context class was implemented with the help of Proto's proto::callable_context<>. If we were to implement it from scratch, it would look something like this:
Hello Calculator 一節中,我們看過一個用戶自定義上下文的例子,用於對計算器表達式進行求值。那個上下文類是在 Proto 的 proto::callable_context<> 的幫助下實現的。如果我們要從零開始實現它,它看起來會是像這樣:

// The calculator_context from the "Hello Calculator" section,
// implemented from scratch.
// 從零開始實現"Hello Calculator"一節中的calculator_context。
struct calculator_context { // The values with which we'll replace the placeholders
// 我們準備用於替換佔位符的值。
std::vector<double> args; template< typename Expr // defaulted template parameters, so we can
// specialize on the expressions that need
// special handling.
// 缺省的模板參數,這樣我們就可以對需要特殊處理的表達式進行特化。
, typename Tag = typename proto::tag_of<Expr>::type , typename Arg0 = typename proto::child_c<Expr, 0>::type > struct eval; // Handle placeholder terminals here... 在此處理佔位符終結符...
template<typename Expr, int I> struct eval<Expr, proto::tag::terminal, placeholder<I> > { typedef double result_type; result_type operator()(Expr &, MyContext &ctx) const { return ctx.args[I]; } }; // Handle other terminals here... 在此處理其它終結符...
template<typename Expr, typename Arg0> struct eval<Expr, proto::tag::terminal, Arg0> { typedef double result_type; result_type operator()(Expr &expr, MyContext &) const { return proto::child(expr); } }; // Handle addition here... 在此處理加法...
template<typename Expr, typename Arg0> struct eval<Expr, proto::tag::plus, Arg0> { typedef double result_type; result_type operator()(Expr &expr, MyContext &ctx) const { return proto::eval(proto::left(expr), ctx) + proto::eval(proto::right(expr), ctx); } }; // ... other eval<> specializations for other node types ...
// ...對於其它節點類型的eval<>特化...
};

Now we can use proto::eval() with the context class above to evaluate calculator expressions as follows:
現在我們可以以這個上下文類來用 proto::eval() 對計算器表達式進行求值,如下:

// Evaluate an expression with a calculator_context 
// 用一個calculator_context對一個表達式進行求值
calculator_context ctx; ctx.args.push_back(5); ctx.args.push_back(6); double d = proto::eval(_1 + _2, ctx); assert(11 == d);

Defining a context from scratch this way is tedious and verbose, but it gives you complete control over how the expression is evaluated. The context class in the Hello Calculator example was much simpler. In the next section we'll see the helper class Proto provides to ease the job of implementing context classes.
這種從零開始定義一個上下文的方法是沉悶而冗長的,但是它可以讓你完成控制如何對表達式進行求值。在 Hello Calculator 的例子中的上下文類更為簡單。在下一節,我們將看到 Proto 所提供的輔助類,它們可以讓實現上下文類的工作變得更為容易。

Proto provides some ready-made context classes that you can use as-is, or that you can use to help while implementing your own contexts. They are:
Proto 提供了一些可用的上下文類,你可以原樣照用,或者用它們來幫助你實現自己的上下文。它們包括:

default_context

An evaluation context that assigns the usual C++ meanings to all the operators. For example, addition nodes are handled by evaluating the left and right children and then adding the results. The proto::default_context uses Boost.Typeof to deduce the types of the expressions it evaluates.
為所有操作符賦予通常的C++意義的求值上下文。例如,加法節點的處理方法是,對左、右子節點求值,然後將結果相加。proto::default_context 使用了 Boost.Typeof 來推斷所求值的表達式的類型。

null_context

A simple context that recursively evaluates children but does not combine the results in any way and returns void.
一個簡單的上下文,遞歸地對子節點進行求值,但不以任何方式對結果進行組合,返回 void。

callable_context<>

A helper that simplifies the job of writing context classes. Rather than writing template specializations, with proto::callable_context<> you write a function object with an overloaded function call operator. Any expressions not handled by an overload are automatically dispatched to a default evaluation context that you can specify.
幫助簡化編寫上下文類的工作的助手。使用 proto::callable_context<>,不再需要編寫模板特化,而是以重載的函數調用操作符編寫一個函數對象。任何沒有被重載進行處理的表達式將自動分派至你指定的某個缺省求值上下文。

The proto::default_context is an evaluation context that assigns the usual C++ meanings to all the operators. For example, addition nodes are handled by evaluating the left and right children and then adding the results. The proto::default_context uses Boost.Typeof to deduce the types of the expressions it evaluates.
proto::default_context 是一個為所有操作符賦予普通的C++意義的求值上下文。例如,加法節點的處理方法是,對左、右子節點求值,然後將結果相加。proto::default_context 使用了 Boost.Typeof 來推斷所求值的表達式的類型。

For example, consider the following "Hello World" example:
例如,考慮以下 "Hello World" 示例:

#include <iostream>
#include <boost/proto/proto.hpp>
#include <boost/proto/context.hpp>
#include <boost/typeof/std/ostream.hpp>
using namespace boost;

proto::terminal< std::ostream & >::type cout_ = { std::cout };

template< typename Expr >
void evaluate( Expr const & expr )
{
    // Evaluate the expression with default_context,
// to give the operators their C++ meanings:
// 以default_context對表達式求值,給予操作符本身的C++意義:
proto::default_context ctx; proto::eval(expr, ctx); } int main() { evaluate( cout_ << "hello" << ',' << " world" ); return 0; }

This program outputs the following:
該程序的輸出如下:

hello, world

proto::default_context is trivially defined in terms of a default_eval<> template, as follows:
proto::default_context 是很簡單地根據一個 default_eval<> 模板來定義的,如下:

// Definition of default_context  default_context的定義
struct default_context { template<typename Expr> struct eval : default_eval< Expr , default_context const , typename tag_of<Expr>::type > {}; };

There are a bunch of default_eval<> specializations, each of which handles a different C++ operator. Here, for instance, is the specialization for binary addition:
有一組 default_eval<> 特化,每一個特化處理一個C++操作符。例如,以下是對二元加法的特化:

// A default expression evaluator for binary addition 對二元加法的缺省表達式求值器
template<typename Expr, typename Context> struct default_eval<Expr, Context, proto::tag::plus> { private: static Expr & s_expr; static Context & s_ctx; public: typedef decltype( proto::eval(proto::child_c<0>(s_expr), s_ctx) + proto::eval(proto::child_c<1>(s_expr), s_ctx) ) result_type; result_type operator ()(Expr &expr, Context &ctx) const { return proto::eval(proto::child_c<0>(expr), ctx) + proto::eval(proto::child_c<1>(expr), ctx); } };

The above code uses decltype to calculate the return type of the function call operator. decltype is a new keyword in the next version of C++ that gets the type of any expression. Most compilers do not yet support decltype directly, so default_eval<> uses the Boost.Typeof library to emulate it. On some compilers, that may mean that default_context either doesn't work or that it requires you to register your types with the Boost.Typeof library. Check the documentation for Boost.Typeof to see.
以上代碼使用了 decltype 來計算函數調用操作符的返回類型。decltype 是下一個C++版本中的新關鍵字,它取出任一表達式的類型。多數編譯器尚未直接支持 decltype,所以 default_eval<> 使用 Boost.Typeof 庫來模擬它。在某些編譯器上,這可能意味著 default_context 不能工作,或是要求你要將你的類型註冊到 Boost.Typeof 庫。請查閱 Boost.Typeof 相關文檔。

The proto::null_context<> is a simple context that recursively evaluates children but does not combine the results in any way and returns void. It is useful in conjunction with callable_context<>, or when defining your own contexts which mutate an expression tree in-place rather than accumulate a result, as we'll see below.
proto::null_context<> 是一個簡單的上下文,遞歸地對子節點進行求值,但不以任何方式對結果進行組合,返回 void。它可以與 callable_context<> 一起使用,或是在定義那些要就地修改表達式樹而不是要計算結果的上下文時使用,稍後我們將看到相關示例。

proto::null_context<> is trivially implemented in terms of null_eval<> as follows:
proto::null_context<> 是很簡單地根據 null_eval<> 來實現的,如下:

// Definition of null_context  null_context的定義
struct null_context { template<typename Expr> struct eval : null_eval<Expr, null_context const, Expr::proto_arity::value> {}; };

And null_eval<> is also trivially implemented. Here, for instance is a binary null_eval<>:
null_eval<> 的實現也很簡單。例如,以上是一個二元的 null_eval<>

// Binary null_eval<>  二元null_eval<>
template<typename Expr, typename Context> struct null_eval<Expr, Context, 2> { typedef void result_type; void operator()(Expr &expr, Context &ctx) const { proto::eval(proto::child_c<0>(expr), ctx); proto::eval(proto::child_c<1>(expr), ctx); } };

When would such classes be useful? Imagine you have an expression tree with integer terminals, and you would like to increment each integer in-place. You might define an evaluation context as follows:
這樣的類在什麼時候有用呢?想像一下,你有一個帶整數終結符的表達式樹,你想就地將每個整數加一。你可以如下定義一個求值上下文:

struct increment_ints
{
    // By default, just evaluate all children by delegating
// to the null_eval<>
// 缺省時,委派至null_eval<>時只對子節點求值
template<typename Expr, typename Arg = proto::result_of::child<Expr>::type> struct eval : null_eval<Expr, increment_ints const> {}; // Increment integer terminals 將整數終結符加一
template<typename Expr> struct eval<Expr, int> { typedef void result_type; void operator()(Expr &expr, increment_ints const &) const { ++proto::child(expr); } }; };

In the next section on proto::callable_context<>, we'll see an even simpler way to achieve the same thing.
在下一節的 proto::callable_context<> 中,我們將看到一個完成相同任務的更為簡單的方法。

The proto::callable_context<> is a helper that simplifies the job of writing context classes. Rather than writing template specializations, with proto::callable_context<> you write a function object with an overloaded function call operator. Any expressions not handled by an overload are automatically dispatched to a default evaluation context that you can specify.
proto::callable_context<> 是一個幫助簡化編寫上下文類的工作的助手。使用 proto::callable_context<>,不再需要編寫模板特化,而是以重載的函數調用操作符編寫一個函數對象。任何沒有被重載進行處理的表達式將自動分派至你指定的某個缺省求值上下文。

Rather than an evaluation context in its own right, proto::callable_context<> is more properly thought of as a context adaptor. To use it, you must define your own context that inherits from proto::callable_context<>.
與其說它是一個求值上下文,不如說 proto::callable_context<> 是一個上下文適配器更為恰當。要使用它,你必須從 proto::callable_context<> 派生你自己的上下文。

In the null_context section, we saw how to implement an evaluation context that increments all the integers within an expression tree. Here is how to do the same thing with the proto::callable_context<>:
null_context 一節中,我們看到了如何實現一個對表達式樹中所有整數進行加一運算的求值上下文。以下是如何用 proto::callable_context<> 完成相同任務的方法:

// An evaluation context that increments all
// integer terminals in-place.
// 將所有整數終結符就地加一的求值上下文
struct increment_ints : callable_context< increment_ints const // derived context 派生的上下文
, null_context const // fall-back context 後傳的上下文
> { typedef void result_type; // Handle int terminals here: 在此處理終結符
void operator()(proto::tag::terminal, int &i) const { ++i; } };

With such a context, we can do the following:
用這個上下文,我們可以做:

literal<int> i = 0, j = 10;
proto::eval( i - j * 3.14, increment_ints() );

std::cout << "i = " << i.get() << std::endl;
std::cout << "j = " << j.get() << std::endl;

This program outputs the following, which shows that the integers i and j have been incremented by 1:
該程序輸出如下,其中顯示整數 ij 都加了 1:

i = 1
j = 11

In the increment_ints context, we didn't have to define any nested eval<> templates. That's because proto::callable_context<> implements them for us. proto::callable_context<> takes two template parameters: the derived context and a fall-back context. For each node in the expression tree being evaluated, proto::callable_context<> checks to see if there is an overloaded operator() in the derived context that accepts it. Given some expression expr of type Expr, and a context ctx, it attempts to call:
在 increment_ints 上下文中,我們不需要定義嵌套的 eval<> 模板。這是因為 proto::callable_context<> 已經為我們實現了它們。proto::callable_context<> 帶有兩個模板參數:派生的上下文和後傳的上下文。對於被求值表達式樹中的每個節點,proto::callable_context<> 檢查在派生上下文中是否有一個重載的 operator() 接受它。給定類型為 Expr 的表達式 expr,以及一個上下文 ctx,它嘗試調用:

ctx(
    typename Expr::proto_tag()
  , proto::child_c<0>(expr)
  , proto::child_c<1>(expr)
    ...
);

Using function overloading and metaprogramming tricks, proto::callable_context<> can detect at compile-time whether such a function exists or not. If so, that function is called. If not, the current expression is passed to the fall-back evaluation context to be processed.
通過使用函數重載和元編程技巧,proto::callable_context<> 可以在編譯期檢測出是否有一個這樣的函數存在。如果有,則調用該函數。如果沒有,則將當前表達式傳遞給後傳上下文來處理。

We saw another example of the proto::callable_context<> when we looked at the simple calculator expression evaluator. There, we wanted to customize the evaluation of placeholder terminals, and delegate the handling of all other nodes to the proto::default_context. We did that as follows:
我們來看 proto::callable_context<> 的另一個例子,我們看一個簡單的計算器表達式求值器。我們想定制對佔位符終結符的求值,並將所有其它節點的處理委派給 proto::default_context。我們可以這樣做:

// An evaluation context for calculator expressions that
// explicitly handles placeholder terminals, but defers the
// processing of all other nodes to the default_context.
// 一個計算器表達式的求值上下文,顯式地處理佔位符終結符,而將所有其它節點的處理交給 default_context.
struct calculator_context : proto::callable_context< calculator_context const > { std::vector<double> args; // Define the result type of the calculator. 定義計算器的返回類型。
typedef double result_type; // Handle the placeholders: 處理佔位符:
template<int I> double operator()(proto::tag::terminal, placeholder<I>) const { return this->args[I]; } };

In this case, we didn't specify a fall-back context. In that case, proto::callable_context<> uses the proto::default_context. With the above calculator_context and a couple of appropriately defined placeholder terminals, we can evaluate calculator expressions, as demonstrated below:
在這個例子中,我們沒有指定後傳上下文。為此,proto::callable_context<> 使用了 proto::default_context。使用以上 calculator_context 以及一些適當定義的佔位符終結符,我們可以對計算器表達式求值,示範如下:

template<int I>
struct placeholder
{};

terminal<placeholder<0> >::type const _1 = {{}};
terminal<placeholder<1> >::type const _2 = {{}};
// ...
calculator_context ctx; ctx.args.push_back(4); ctx.args.push_back(5); double j = proto::eval( (_2 - _1) / _2 * 100, ctx ); std::cout << "j = " << j << std::endl;

The above code displays the following:
以上代碼輸出如下:

j = 20

If you have ever built a parser with the help of a tool like Antlr, yacc or Boost.Spirit, you might be familiar with semantic actions. In addition to allowing you to define the grammar of the language recognized by the parser, these tools let you embed code within your grammar that executes when parts of the grammar participate in a parse. Proto has the equivalent of semantic actions. They are called transforms. This section describes how to embed transforms within your Proto grammars, turning your grammars into function objects that can manipulate or evaluate expressions in powerful ways.
如果你曾經在某個工具,如 Antlr, yacc 或 Boost.Spirit,的幫助下構建過一個詞法分析器,你可能會知道語義動作。除了可以讓你定義詞法分析器所識別的語言的語法之外,這些工具還可以讓你將一些代碼嵌入到你的語法中,當這部分語法在分析時可以執行這些代碼。Proto 也有與語義動作相當的東西。它們被稱為變換。本節將描述如何將變換嵌入到你的 Proto 語法中,將你的語法變為函數對象,以更強大的方式來操縱表達式和進行求值。

Proto transforms are an advanced topic. We'll take it slow, using examples to illustrate the key concepts, starting simple.
Proto 變換是一個高級議題。我們會慢慢講,用一些例子來示範其中的關鍵概念,先從簡單的開始。

The Proto grammars we've seen so far are static. You can check at compile-time to see if an expression type matches a grammar, but that's it. Things get more interesting when you give them runtime behaviors. A grammar with embedded transforms is more than just a static grammar. It is a function object that accepts expressions that match the grammar and does something with them. 
到 目前為止,我們見到的 Proto 語法都是靜態的。你可以在編譯期檢查某個表達式類型是否與語法匹配,僅此而已。如果你給予它們運行期行為,則事情會更有趣。嵌入了變換的語法不再僅僅是一 個靜態語法而已。它是一個函數對象,接受與此語法匹配的表達式,並會對它們做些事情。

Below is a very simple grammar. It matches terminal expressions.
以下是一個更深簡單的語法。它匹配終結符表達式。

// A simple Proto grammar that matches all terminals
// 一個匹配所有終結符的簡單的 Proto 語法
proto::terminal< _ >

Here is the same grammar with a transform that extracts the value from the terminal:
以下是一個相同的語法,帶有一個從終結符取出值的變換:

// A simple Proto grammar that matches all terminals
// *and* a function object that extracts the value from
// the terminal
// 一個匹配所有終結符的簡單的Proto語法,以及一個從終結符取出值的函數對像
proto::when< proto::terminal< _ > , proto::_value // <-- Look, a transform! 看,這是一個變換!
>

You can read this as follows: when you match a terminal expression, extract the value. The type proto::_value is a so-called transform. Later we'll see what makes it a transform, but for now just think of it as a kind of function object. Note the use of proto::when<>: the first template parameter is the grammar to match and the second is the transform to execute. The result is both a grammar that matches terminal expressions and a function object that accepts terminal expressions and extracts their values.
你可以之樣來讀它:當你匹配了一個終結符表達式,取出它的值。類型 proto::_value 是一個所謂的變換。稍後我們將看到是什麼使得它成為一個變換,不過現在我們只把它想像為一種函數對象。留意 proto::when<> 的用法:第一個模板參數是要匹配的語法,第二個是要執行的變換。其結果既是一個匹配終結符表達式的語法,也是一個接受終結符表達式並返回其值的函數對象。

As with ordinary grammars, we can define an empty struct that inherits from a grammar+transform to give us an easy way to refer back to the thing we're defining, as follows:
和普通的語法一樣,我們可以定義一個空的結構,派生自一個語法+變換,這樣可以很容易地引用回我們定義的東西,如下:

// A grammar and a function object, as before
// 一個語法和一個函數對象,如前
struct Value : proto::when< proto::terminal< _ > , proto::_value > {}; // "Value" is a grammar that matches terminal expressions
// 」Value"是一個匹配終結符表達式的語法
BOOST_MPL_ASSERT(( proto::matches< proto::terminal<int>::type, Value > )); // "Value" also defines a function object that accepts terminals
// and extracts their value.
// "Value"也定義了一個接受一個終結符並取出其值的函數對象。
proto::terminal<int>::type answer = {42}; Value get_value; int i = get_value( answer );

As already mentioned, Value is a grammar that matches terminal expressions and a function object that operates on terminal expressions. It would be an error to pass a non-terminal expression to the Value function object. This is a general property of grammars with transforms; when using them as function objects, expressions passed to them must match the grammar.
前面已經提過,Value 是一個匹配終結符表達式的語法,也是一個操作於終結符表達式之上的函數對象。傳遞一個非終結符表達式給 Value 函數對象是錯誤的。這是帶有變換的語法的基本特性;把它們當作函數對像使用時,傳遞給它們的表達式必須符合相應的語法。

Proto grammars are valid TR1-style function objects. That means you can use boost::result_of<> to ask a grammar what its return type will be, given a particular expression type. For instance, we can access the Value grammar's return type as follows:
Proto 語法是有效的TR1風格的函數對象。這意味著你可以用 boost::result_of<> 來詢問某個語法,給定一個特定的表達式類型,它的返回類型是什麼。例如,我們可以如下獲得 Value 語法的返回類型:

// We can use boost::result_of<> to get the return type
// of a Proto grammar.
// 我們可以用 boost::result_of<> 來獲得一個 Proto 語法的返回類型
typedef typename boost::result_of<Value(proto::terminal<int>::type)>::type result_type; // Check that we got the type we expected
// 檢查我們獲得的類型是否所期望的
BOOST_MPL_ASSERT(( boost::is_same<result_type, int> ));
[Note] Note 備註

A grammar with embedded transforms is both a grammar and a function object. Calling these things "grammars with transforms" would get tedious. We could call them something like "active grammars", but as we'll see every grammar that you can define with Proto is "active"; that is, every grammar has some behavior when used as a function object. So we'll continue calling these things plain "grammars". The term "transform" is reserved for the thing that is used as the second parameter to the proto::when<> template.
一個帶有內嵌變換的語法既是一個語法,也是一個函數對象。把它稱為"帶變換的語法"太過麻煩。我們可以稱之為"主動語法",不過,就像我們即將看到的那樣,你用 Proto 定義的每一個語法都是"主動"的;即,每一個語法被作為函數對像使用時都會有一些行為。所以,我們繼續把它們稱為"語法"就算了。術語"變換"保留作為對 proto::when<> 模板的第二個參數的稱呼。

Most grammars are a little more complicated than the one in the preceding section. For the sake of illustration, let's define a rather nonsensical grammar that matches any expression and recurses to the leftmost terminal and returns its value. It will demonstrate how two key concepts of Proto grammars -- alternation and recursion -- interact with transforms. The grammar is described below.
多數語法要比上一節中的複雜一點。為了示範之用,我們來定義一個荒謬的語法,它匹配任意的表達式,遞歸至最左側的終結符並返回其值。它將示範 Proto 語法的兩個關鍵概念 -- 多選與遞歸 -- 是如何與變換相結合的。該語法表示如下。

// A grammar that matches any expression, and a function object
// that returns the value of the leftmost terminal.
// 一個匹配任意表達式的語法,以及一個返回最左側終結符的值的函數對象。
struct LeftmostLeaf : proto::or_< // If the expression is a terminal, return its value
// 如果該表達式是一個終結符,則返回它的值
proto::when< proto::terminal< _ > , proto::_value > // Otherwise, it is a non-terminal. Return the result
// of invoking LeftmostLeaf on the 0th (leftmost) child.
// 否則,它不是一個終結符。返回對第0個(最左邊的)子節點調用LeftmostLeaf的結果。
, proto::when< _ , LeftmostLeaf( proto::_child0 ) > > {}; // A Proto terminal wrapping std::cout 包裝了std::cout的Proto終結符
proto::terminal< std::ostream & >::type cout_ = { std::cout }; // Create an expression and use LeftmostLeaf to extract the
// value of the leftmost terminal, which will be std::cout.
// 創建一個表達式並用LeftmostLeaf來取出最左側終結符的值,即std::cout。
std::ostream & sout = LeftmostLeaf()( cout_ << "the answer: " << 42 << '\n' );

We've seen proto::or_<> before. Here it is serving two roles. First, it is a grammar that matches any of its alternate sub-grammars; in this case, either a terminal or a non-terminal. Second, it is also a function object that accepts an expression, finds the alternate sub-grammar that matches the expression, and applies its transform. And since LeftmostLeaf inherits from proto::or_<>, LeftmostLeaf is also both a grammar and a function object.
前面我們已經見過 proto::or_<> 了。在這裡它扮演兩個角色。首先,它是一個匹配其任一子語法的語法;在這個例子中,即為終結符或非終結符。其次,它也是一個函數對象,接受一個表達式,找出與該表達式相匹配的子語法,並對它應用變換。由於 LeftmostLeaf 繼承自 proto::or_<>,所以 LeftmostLeaf 既是一個語法,也是一個函數對象。

[Note] Note 備註

The second alternate uses proto::_ as its grammar. Recall that proto::_ is the wildcard grammar that matches any expression. Since alternates in proto::or_<> are tried in order, and since the first alternate handles all terminals, the second alternate handles all (and only) non-terminals. Often enough, proto::when< _, some-transform > is the last alternate in a grammar, so for improved readability, you could use the equivalent proto::otherwise< some-transform >.
第二個選擇以 proto::_ 作為其語法。回想一下,proto::_ 是匹配任意表達式的通配語法。由於在 proto::or_<> 中的候選項是按順序嘗試的,而且第一個候選項對應所有終結符,所以第二個候選項代表所有(也僅僅是)非終結符。通常來說,proto::when< _, some-transform > 作為語法中的最後一個候選項就足夠了,如果要提高可讀性,你可以用等價的 proto::otherwise< some-transform >.

The next section describes this grammar further.
下一節將進一步討論這個語法。

In the grammar defined in the preceding section, the transform associated with non-terminals is a little strange-looking:
在前一節中定義的語法中,與非終結符相關聯的變換看起來有些奇怪:

proto::when<
    _
  , LeftmostLeaf( proto::_child0 )   // <-- a "callable" transform 一個"可調用"變換
>

It has the effect of accepting non-terminal expressions, taking the 0th (leftmost) child and recursively invoking the LeftmostLeaf function on it. But LeftmostLeaf( proto::_child0 ) is actually a function type. Literally, it is the type of a function that accepts an object of type proto::_child0 and returns an object of type LeftmostLeaf. So how do we make sense of this transform? Clearly, there is no function that actually has this signature, nor would such a function be useful. The key is in understanding how proto::when<> interprets its second template parameter.
其中含有對於非終結符表達式的動作,取出第0個(最左邊)子節點並對其遞歸調用 LeftmostLeaf 函數。但是 LeftmostLeaf( proto::_child0 ) 是一個實實在在的函數類型啊。從字面上看,它是一個函數的類型,該函數接受一個類型為 proto::_child0 的對象並返回一個類型為 LeftmostLeaf 的對象。那麼我們要如何弄清楚這個變換的意思呢?無疑,並沒有一個函數真的具有這個簽名,這樣的函數也沒有用。關鍵在於要弄明白 proto::when<> 如何解釋它的第二個模板參數。

When the second template parameter to proto::when<> is a function type, proto::when<> interprets the function type as a transform. In this case, LeftmostLeaf is treated as the type of a function object to invoke, and proto::_child0 is treated as a transform. First, proto::_child0 is applied to the current expression (the non-terminal that matched this alternate sub-grammar), and the result (the 0th child) is passed as an argument to LeftmostLeaf.
如果傳遞給 proto::when<> 的第二個模板參數是一個函數類型,則 proto::when<> 將該函數類型解釋為一個變換。在這個例子中,LeftmostLeaf 被視為要調用的函數對象的類型,而 proto::_child0 則被視為一個變換。首先,將 proto::_child0 應用至當前表達式(匹配該被選子語法的非終結符),然後將結果(第0個子節點)作為參數傳遞給 LeftmostLeaf

[Note] Note 備註

Transforms are a Domain-Specific Language 
變換是一種領域專用語言

LeftmostLeaf( proto::_child0 ) looks like an invocation of the LeftmostLeaf function object, but it's not, but then it actually is! Why this confusing subterfuge? Function types give us a natural and concise syntax for composing more complicated transforms from simpler ones. The fact that the syntax is suggestive of a function invocation is on purpose. It is a domain-specific embedded language for defining expression transformations. If the subterfuge worked, it may have fooled you into thinking the transform is doing exactly what it actually does! And that's the point.
LeftmostLeaf( proto::_child0 ) 看起來就像對 LeftmostLeaf 函數對象的一次調用,但它不是,然後實際上它就是!為什麼說的如此混亂?函數類型給了我們一個自然且簡潔的語法來從簡單的變換組合出複雜的變換。事實上, 這個語法只是某個函數的提示,調用才是目的。它是一種用於定義表達式變換的領域專用語言。如果這種托詞可以成功,它可能會蒙蔽你,使你以為變換在做的其實 就是它實際上要做的!這就是重點。

The type LeftmostLeaf( proto::_child0 ) is an example of a callable transform. It is a function type that represents a function object to call and its arguments. The types proto::_child0 and proto::_value are primitive transforms. They are plain structs, not unlike function objects, from which callable transforms can be composed. There is one other type of transform, object transforms, that we'll encounter next.
類型 LeftmostLeaf( proto::_child0 )可調用變換 的一個例子。它是一個函數類型,表示一個要調用的函數對象及其參數。類型 proto::_child0proto::_value 則是 基本變換。它們是普通的結構,不像函數對象,可以由它們組合出可調用變換。另外還有一種變換類型,對像變換,後面我們將要提到。

The very first transform we looked at simply extracted the value of terminals. Let's do the same thing, but this time we'll promote all ints to longs first. (Please forgive the contrived-ness of the examples so far; they get more interesting later.) Here's the grammar:
我們最早見到的一個變換只是取出終結符的值。下面我們再來做同樣的事情,不過這次我們先要將所有 int 提升為 long。(請忘記到目前為止的這些例子的做作性;稍後它們會更有趣些)。以下是相應語法:

// A simple Proto grammar that matches all terminals,
// and a function object that extracts the value from
// the terminal, promoting ints to longs:
// 一個簡單的Proto語法,匹配所有終結符,以及一個從終結符取出值的函數對象,將int提升為long:
struct ValueWithPomote : proto::or_< proto::when< proto::terminal< int > , long(proto::_value) // <-- an "object" transform 一個"對像"變換
> , proto::when< proto::terminal< _ > , proto::_value > > {};

You can read the above grammar as follows: when you match an int terminal, extract the value from the terminal and use it to initialize a long; otherwise, when you match another kind of terminal, just extract the value. The type long(proto::_value) is a so-called object transform. It looks like the creation of a temporary long, but it's really a function type. Just as a callable transform is a function type that represents a function to call and its arguments, an object transforms is a function type that represents an object to construct and the arguments to its constructor.
你可這樣來讀以上語法:如果你匹配到一個int終結符,就從終結符中取出它的值,並用它來初始化一個long;否則,如果你匹配到其它類型的終結符,則只是取出它的值。類型 long(proto::_value) 是一個所謂的 對像變換。它看起來像是創建一個臨時的long,其實它是一個函數類型。和可調用變換是一個表示了要調用的函數及其參數的函數類型一樣,對像變換是一個表示了要構造的對象及其構造函數的參數的函數類型。

[Note] Note 備註

Object Transforms vs. Callable Transforms 
對像變換 vs. 可調用變換

When using function types as Proto transforms, they can either represent an object to construct or a function to call. It is similar to "normal" C++ where the syntax foo("arg") can either be interpreted as an object to construct or a function to call, depending on whether foo is a type or a function. But consider two of the transforms we've seen so far:
把函數類型用作 Proto 變換時,它們可以表示一個要構造的對象,或者表示一個要調用的函數。這類似於"普通"的C++,語法 foo("arg") 可以解釋為一個要構造的對象,或者是一個要調用的函數,這取決於 foo 是一個類型還是一個函數。不過,考慮一下我們已見到的兩個變換:

LeftmostLeaf(proto::_child0)  // <-- a callable transform
long(proto::_value) // <-- an object transform

Proto can't know in general which is which, so it uses a trait, proto::is_callable<>, to differentiate. is_callable< long >::value is false so long(proto::_value) is an object to construct, but is_callable< LeftmostLeaf >::value is true so LeftmostLeaf(proto::_child0) is a function to call. Later on, we'll see how Proto recognizes a type as "callable".
Proto 通常並不知道哪個是哪個,所以它使用了 trait,proto::is_callable<>,來區分。is_callable< long >::value 為false,所以 long(proto::_value) 是一個要構造的對象,而 is_callable< LeftmostLeaf >::value 為true,所以 LeftmostLeaf(proto::_child0) 是一個要調用的函數。接著,我們將會看到 Proto 是如何把一個類型識別為"可調用"的。

Now that we have the basics of Proto transforms down, let's consider a slightly more realistic example. We can use transforms to improve the type-safety of the calculator DSEL. If you recall, it lets you write infix arithmetic expressions involving argument placeholders like _1 and _2 and pass them to STL algorithms as function objects, as follows:
現在我們已經有了 Proto 變換的基礎,讓我們來考慮一下更現實一些例子。我們可以用變換來改進 計算器 DSEL 的類型安全性。如果你還記得,它可以讓你編寫含有參數佔位符,如 _1_2,的中綴算術表達式,並將表達式作為函數對像傳遞給STL算法,如下:

double a1[4] = { 56, 84, 37, 69 };
double a2[4] = { 65, 120, 60, 70 };
double a3[4] = { 0 };

// Use std::transform() and a calculator expression
// to calculate percentages given two input sequences:
// 用std::transform()和一個計算器表達式對給定的兩個輸入序列計算百分比:
std::transform(a1, a1+4, a2, a3, (_2 - _1) / _2 * 100);

This works because we gave calculator expressions an operator() that evaluates the expression, replacing the placeholders with the arguments to operator(). The overloaded calculator<>::operator() looked like this:
這可以是因為我們給了計算器表達式一個 operator() 來對表達式進行求值並以傳給 operator() 的參數替換相應的佔位符。重載的 calculator<>::operator() 看起來如下:

// Overload operator() to invoke proto::eval() with
// our calculator_context.
// 重載operator(),以我們的calculator_context調用proto::eval()。
template<typename Expr> double calculator<Expr>::operator()(double a1 = 0, double a2 = 0) const { calculator_context ctx; ctx.args.push_back(a1); ctx.args.push_back(a2); return proto::eval(*this, ctx); }

Although this works, it's not ideal because it doesn't warn users if they supply too many or too few arguments to a calculator expression. Consider the following mistakes:
雖然這樣做可以,但是它還不是完美的,因為如果向計算器表達式提供了過多或過少的參數,它不會給出警告。考慮以下錯誤:

(_1 * _1)(4, 2);  // Oops, too many arguments! 喔,太多參數了!
(_2 * _2)(42); // Oops, too few arguments! 喔,太少參數了!

The expression _1 * _1 defines a unary calculator expression; it takes one argument and squares it. If we pass more than one argument, the extra arguments will be silently ignored, which might be surprising to users. The next expression, _2 * _2 defines a binary calculator expression; it takes two arguments, ignores the first and squares the second. If we only pass one argument, the code silently fills in 0.0 for the second argument, which is also probably not what users expect. What can be done?
表達式 _1 * _1 定義了一個單參數的計算器表達式;它接受一個參數並對它求平方。如果我們傳入一個以上的參數,則額外的參數會被悄悄地忽略掉,這可能會令用戶驚訝。第二個表達式,_2 * _2 定義了一個二元計算器表達式;它接受兩個參數,忽略其中第一個而對第二個求平方。如果我們只傳入一個參數,代碼會悄悄地將 0.0 填入第二個參數,這同樣可能不是用戶想要的。那麼可以怎麼做呢?

We can say that the arity of a calculator expression is the number of arguments it expects, and it is equal to the largest placeholder in the expression. So, the arity of _1 * _1 is one, and the arity of _2 * _2 is two. We can increase the type-safety of our calculator DSEL by making sure the arity of an expression equals the actual number of arguments supplied. Computing the arity of an expression is simple with the help of Proto transforms.
我們可以說,一個計算器表達式的 arity 是它所期望的參數數量,它等於表達式中最大的佔位符。因此,_1 * _1 的arity為1,而 _2 * _2 的arity為2。我們可以通過確認一個表達式的arity是否等於實際提供的參數數量來提高我們的計算器DSEL的類型安全性。有了 Proto 變換的幫助,計算一個表達式的arity很簡單。

It's straightforward to describe in words how the arity of an expression should be calculated. Consider that calculator expressions can be made of _1, _2, literals, unary expressions and binary expressions. The following table shows the arities for each of these 5 constituents.
用語言來表達如何計算一個表達式的arity非常簡單。考慮到計算器表達式可以由 _1, _2, 字面值,單參數表達式和二元表達式組成。下表展示了這5種情況的arity。

Table 14.6. Calculator Sub-Expression Arities
表 14.6. 計算器子表達式的arity

Sub-Expression 子表達式

Arity

Placeholder 1 佔位符1

1

Placeholder 2 佔位符2

2

Literal 字面值

0

Unary Expression 單參數表達式

arity of the operand 操作數的arity

Binary Expression 二元表達式

max arity of the two operands 兩個操作數的arity的最大值


Using this information, we can write the grammar for calculator expressions and attach transforms for computing the arity of each constituent. The code below computes the expression arity as a compile-time integer, using integral wrappers and metafunctions from the Boost MPL Library. The grammar is described below.
使用這些信息,我們可以寫出計算器表達式的語法並附上計算每種情形的arity的變換。以下代碼將表達式的arity計算為一個編譯期整數,使用來自Boost MPL庫的整數包裝器和元函數。語法描述如下。

struct CalcArity
  : proto::or_<
        proto::when< proto::terminal< placeholder<0> >,
            mpl::int_<1>()
        >
      , proto::when< proto::terminal< placeholder<1> >,
            mpl::int_<2>()
        >
      , proto::when< proto::terminal<_>,
            mpl::int_<0>()
        >
      , proto::when< proto::unary_expr<_, CalcArity>,
            CalcArity(proto::_child)
        >
      , proto::when< proto::binary_expr<_, CalcArity, CalcArity>,
            mpl::max<CalcArity(proto::_left),
                     CalcArity(proto::_right)>()
        >
    >
{};

When we find a placeholder terminal or a literal, we use an object transform such as mpl::int_<1>() to create a (default-constructed) compile-time integer representing the arity of that terminal.
當我們找到一個佔位符終結符或一個字面值時,我們使用一個形如 mpl::int_<1>()對像變換 來創建一個(缺省構造的)編譯期整數,表示這個終結符的arity。

For unary expressions, we use CalcArity(proto::_child) which is a callable transform that computes the arity of the expression's child.
對於單參數表達式,我們使用 CalcArity(proto::_child),它是一個計算該表達式的子節點的arity的可調用變換

The transform for binary expressions has a few new tricks. Let's look more closely:
二元表達式的變換有點訣竅。我們靠近一點來看:

// Compute the left and right arities and
// take the larger of the two.
// 計算左、右節點的arity,並取出兩者中的較大值。
mpl::max<CalcArity(proto::_left), CalcArity(proto::_right)>()

This is an object transform; it default-constructs ... what exactly? The mpl::max<> template is an MPL metafunction that accepts two compile-time integers. It has a nested ::type typedef (not shown) that is the maximum of the two. But here, we appear to be passing it two things that are not compile-time integers; they're Proto callable transforms. Proto is smart enough to recognize that fact. It first evaluates the two nested callable transforms, computing the arities of the left and right child expressions. Then it puts the resulting integers into mpl::max<> and evaluates the metafunction by asking for the nested ::type. That is the type of the object that gets default-constructed and returned.
這是一個對像變換;它缺省構造了 ... 什麼呢?模板 mpl::max<> 是一個MPL元函數,它接受兩個編譯期整數。它有一個嵌套的 ::type typedef (未示出)表示這兩個整數中的最大者。不過在這裡,我們看到的是,傳給它的是兩個不是編譯期整數的東西;傳入的是 Proto 可調用變換。Proto 是很聰明的,它可以識別出這個事實。它首先對兩個嵌套的可調用變換進行求值,分別計算左、右節點的arity。然後將結果整數放入 mpl::max<> 並通過詢問嵌套的 ::type 來對元函數求值。結果就是獲得缺省構造並返回的對象類型。

More generally, when evaluating object transforms, Proto looks at the object type and checks whether it is a template specialization, like mpl::max<>. If it is, Proto looks for nested transforms that it can evaluate. After any nested transforms have been evaluated and substituted back into the template, the new template specialization is the result type, unless that type has a nested ::type, in which case that becomes the result. 
更一般地說,在對對像變換進行求值時,Proto 會查看對象的類型並檢查它是否為一個模板特化,如 mpl::max<>。如果是,則 Proto 尋找它可以求值的嵌套變換。在所有嵌套變換都求值後,將結果替換回模板中,新的模板特化就是結果類型,除非該類型帶有一個嵌套的 ::type,這種情況下它才是結果。

Now that we can calculate the arity of a calculator expression, let's redefine the calculator<> expression wrapper we wrote in the Getting Started guide to use the CalcArity grammar and some macros from Boost.MPL to issue compile-time errors when users specify too many or too few arguments.
現在,我們可以計算一個計算器表達式的arity了,讓我們用 CalcArity 語法和來自Boost.MPL的一些宏來對我們在"入門"一節中寫過的 calculator<> 表達式包裝器進行重新定義,以便當用戶指定了過多或過少參數時給出編譯期錯誤。

// The calculator expression wrapper, as defined in the Hello
// Calculator example in the Getting Started guide. It behaves
// just like the expression it wraps, but with extra operator()
// member functions that evaluate the expression.
// NEW: Use the CalcArity grammar to ensure that the correct
// number of arguments are supplied.
// 計算器表達式包裝器,類似於在"入門"一節的 Hello Calculator 例子中的定義。
// 其行為類似於所包裝的表達式,但帶有額外的operator()成員函數來對表達式求值。
// 新增:使用CalcArity語法來確保給出的參數數量是正確的。
template<typename Expr> struct calculator : proto::extends<Expr, calculator<Expr>, calculator_domain> { typedef proto::extends<Expr, calculator<Expr>, calculator_domain> base_type; calculator(Expr const &expr = Expr()) : base_type(expr) {} typedef double result_type; // Use CalcArity to compute the arity of Expr:
// 用CalcArity來計算Expr的arity:
static int const arity = boost::result_of<CalcArity(Expr)>::type::value; double operator()() const { BOOST_MPL_ASSERT_RELATION(0, ==, arity); calculator_context ctx; return proto::eval(*this, ctx); } double operator()(double a1) const { BOOST_MPL_ASSERT_RELATION(1, ==, arity); calculator_context ctx; ctx.args.push_back(a1); return proto::eval(*this, ctx); } double operator()(double a1, double a2) const { BOOST_MPL_ASSERT_RELATION(2, ==, arity); calculator_context ctx; ctx.args.push_back(a1); ctx.args.push_back(a2); return proto::eval(*this, ctx); } };

Note the use of boost::result_of<> to access the return type of the CalcArity function object. Since we used compile-time integers in our transforms, the arity of the expression is encoded in the return type of the CalcArity function object. Proto grammars are valid TR1-style function objects, so you can use boost::result_of<> to figure out their return types.
注意,其中使用了 boost::result_of<> 來獲得 CalcArity 函數對象的返回類型。由於我們在變換中使用了編譯期整數,所以表達式的arity是被編碼在 CalcArity 函數對象的返回類型中的。Proto 語法是有效的TR1風格的函數對象,所以你可以用 boost::result_of<> 來取出它的返回類型。

With our compile-time assertions in place, when users provide too many or too few arguments to a calculator expression, as in:
有了我們的編譯期斷言後,當用戶提供過多或過少參數給計算器表達式時,如下:

(_2 * _2)(42); // Oops, too few arguments! 喔,太少參數了!

... they will get a compile-time error message on the line with the assertion that reads something like this [3] :
... 他們將得到一個編譯期的錯誤信息,指向斷言所在的行,提示類似於 [3]

c:\boost\org\trunk\libs\proto\scratch\main.cpp(97) : error C2664: 'boost::mpl::asse
rtion_failed' : cannot convert parameter 1 from 'boost::mpl::failed ************boo
st::mpl::assert_relation<x,y,__formal>::************' to 'boost::mpl::assert<false>
::type'
with
[
x=1,
y=2,
__formal=bool boost::mpl::operator==(boost::mpl::failed,boost::mpl::failed)
]

The point of this exercise was to show that we can write a fairly simple Proto grammar with embedded transforms that is declarative and readable and can compute interesting properties of arbitrarily complicated expressions. But transforms can do more than that. Boost.Xpressive uses transforms to turn expressions into finite state automata for matching regular expressions, and Boost.Spirit uses transforms to build recursive descent parser generators. Proto comes with a collection of built-in transforms that you can use to perform very sophisticated expression manipulations like these. In the next few sections we'll see some of them in action.
這 個練習所展示的觀點是,我們可以編寫一個很簡單的、帶有嵌入變換的、具有描述性和可讀性的 Proto 語法,它可以計算一個任意複雜的表達式的某些你感興趣的屬性。但是變換可以做得更多。Boost.Xpressive 使用變換來將表達式轉為有限狀態機以匹配正則表達式,而 Boost.Spirit 則使用變換來構建遞歸下降分析器的生成器。Proto 配備了一組內建變換,你可以用它們來執行像這樣的一些非常複雜的表達式處理。在下面幾節中,我們將看到其中的一些。

So far, we've only seen examples of grammars with transforms that accept one argument: the expression to transform. But consider for a moment how, in ordinary procedural code, you would turn a binary tree into a linked list. You would start with an empty list. Then, you would recursively convert the right branch to a list, and use the result as the initial state while converting the left branch to a list. That is, you would need a function that takes two parameters: the current node and the list so far. These sorts of accumulation problems are quite common when processing trees. The linked list is an example of an accumulation variable or state. Each iteration of the algorithm takes the current element and state, applies some binary function to the two and creates a new state. In the STL, this algorithm is called std::accumulate(). In many other languages, it is called fold. Let's see how to implement a fold algorithm with Proto transforms.
到 目前為止,我們看到的語法例子中的變換都只是接受一個參數:即進行變換的表達式。但是考慮一下,在普通的程序代碼中,你會如何將一個二叉樹轉換為一個鏈 表。你會從一個空鏈表開始。然後遞歸地將右分支轉換為一個鏈表,並以此結果作為初始狀態,同時將左分支也轉換為一個鏈表。也就是說,你需要一個接受兩個參 數的函數,這兩個參數分別是:當前節點以及到目前為止的鏈表。在處理樹結構時,這一類累計問題是很常見的。這個鏈表是累計變量或狀態的一個例子。該算法的每一次迭代都接受當前元素和狀態,對這兩者應用某些二元函數並創建一個新的狀態。在STL中,這個算法稱為 std::accumulate()。而在許多其它的語言中,它被稱為折疊fold。我們來看看如何用Proto變換來實現一個折疊算法。

All Proto grammars can optionally accept a state parameter in addition to the expression to transform. If you want to fold a tree to a list, you'll need to make use of the state parameter to pass around the list you've built so far. As for the list, the Boost.Fusion library provides a fusion::cons<> type from which you can build heterogeneous lists. The type fusion::nil represents an empty list.
所有的Proto語法除了變換的表達式之外,都接受一個可選的狀態參數。如果你想將一棵樹折疊為一個鏈表,你需要利用這個狀態參數來傳遞迄今你所建立的鏈表。至於這個鏈表,Boost.Fusion 庫提供了一個 fusion::cons<> 類型,從中你可以建立一個異構的鏈表。類型 fusion::nil 則表示一個空的鏈表。

Below is a grammar that recognizes output expressions like cout_ << 42 << '\n' and puts the arguments into a Fusion list. It is explained below.
以下是一個語法,它識別形如 cout_ << 42 << '\n' 的輸出表達式並將參數放入一個 Fusion 鏈表。解釋如下。

// Fold the terminals in output statements like
// "cout_ << 42 << '\n'" into a Fusion cons-list.
// 將形如"cout_ << 42 << '\n'"這樣的輸出語句中的終結符折疊到一個Fusion cons-鏈表中。
struct FoldToList : proto::or_< // Don't add the ostream terminal to the list
// 不要將ostream終結符加到鏈表中
proto::when< proto::terminal< std::ostream & > , proto::_state > // Put all other terminals at the head of the
// list that we're building in the "state" parameter
// 將其它所有終結符放在我們正在"state"參數中構建的鏈表的頭部
, proto::when< proto::terminal<_> , fusion::cons<proto::_value, proto::_state>( proto::_value, proto::_state ) > // For left-shift operations, first fold the right
// child to a list using the current state. Use
// the result as the state parameter when folding
// the left child to a list.
// 對於左移操作,首先將右子節點與當前state折疊到一個鏈表中。
// 然後在將左子節點折疊至鏈表時,把該結果作為state參數。
, proto::when< proto::shift_left<FoldToList, FoldToList> , FoldToList( proto::_left , FoldToList(proto::_right, proto::_state) ) > > {};

Before reading on, see if you can apply what you know already about object, callable and primitive transforms to figure out how this grammar works.
在繼續往下之前,看看你是否可以用你已知的關於對像變換、可調用變換和基本變換的知識來找出這個語法是如何工作的。

When you use the FoldToList function, you'll need to pass two arguments: the expression to fold, and the initial state: an empty list. Those two arguments get passed around to each transform. We learned previously that proto::_value is a primitive transform that accepts a terminal expression and extracts its value. What we didn't know until now was that it also accepts the current state and ignores it. proto::_state is also a primitive transform. It accepts the current expression, which it ignores, and the current state, which it returns.
當你使用 FoldToList 函數時,你需要傳入兩個參數:進行折疊的表達式,以及初始狀態:一個空的鏈表。這兩個參數被傳遞給每一個變換。前面我們已經學過 proto::_value 是一個基本變換,接受一個終結符表達式並了取出其值。我們還不知道的是,它也可以接受當前的狀態並忽略它proto::_state 也是一個基本變換。它接受當前的表達式和當前狀態,但忽略前者而返回後者。

When we find a terminal, we stick it at the head of the cons list, using the current state as the tail of the list. (The first alternate causes the ostream to be skipped. We don't want cout in the list.) When we find a shift-left node, we apply the following transform:
當我們找到一個終結符時,我們把它放在cons鏈表的頭部,並以當前狀態作為鏈表的尾部。(第一個候選項導致 ostream 被跳過。我們不想讓 cout 出現在鏈表中)。當我們找到一個左移節點時,我們使用以下變換:

// Fold the right child and use the result as
// state while folding the right.
// 折疊右子節點並將結果用作折疊左子節點時的狀態。
FoldToList( proto::_left , FoldToList(proto::_right, proto::_state) )

You can read this transform as follows: using the current state, fold the right child to a list. Use the new list as the state while folding the left child to a list.
你可以這樣來解讀這個變換:使用當前狀態,將右子節點折疊至一個鏈表。把這個新鏈表用作狀態參數,將左子節點折疊至一個鏈表。

[Tip] Tip 提示

If your compiler is Microsoft Visual C++, you'll find that the above transform does not compile. The compiler has bugs with its handling of nested function types. You can work around the bug by wrapping the inner transform in proto::call<> as follows:
如果你的編譯器是 Microsoft Visual C++,你會發現以上變換不能通過編譯。該編譯器對於嵌套函數類型的處理存在缺陷。你可以像下面這樣通過將內層的變換包裝在 proto::call<> 中來繞過這一缺陷:

FoldToList(
    proto::_left
  , proto::call<FoldToList(proto::_right, proto::_state)>
)

proto::call<> turns a callable transform into a primitive transform, but more on that later.
proto::call<> 將一個可調用變換變為一個基本變換,稍後會更多用到。

Now that we have defined the FoldToList function object, we can use it to turn output expressions into lists as follows:
現在,我們已經定義了 FoldToList 函數對象,我們可以用它來把輸出表達式轉換為鏈表,如下:

proto::terminal<std::ostream &>::type const cout_ = {std::cout};

// This is the type of the list we build below
// 這是我們後面要構建的鏈表的類型
typedef fusion::cons< int , fusion::cons< double , fusion::cons< char , fusion::nil > > > result_type; // Fold an output expression into a Fusion list, using
// fusion::nil as the initial state of the transformation.
// 將一個輸出表達式折疊至一個Fusion鏈表,以fusion::nil作為變換的初始狀態。
FoldToList to_list; result_type args = to_list(cout_ << 1 << 3.14 << '\n', fusion::nil()); // Now "args" is the list: {1, 3.14, '\n'}
// 現在"args"為鏈表:{1, 3.14, '\n'}

When writing transforms, "fold" is such a basic operation that Proto provides a number of built-in fold transforms. We'll get to them later. For now, rest assured that you won't always have to stretch your brain so far to do such basic things.
在編寫變換時,"折疊"是一個非常基本的操作,所以Proto提供了一些內建的折疊變換。我們稍後將會提及它們。現在你可以放心了,你不需要總是花費腦筋去做這些基本的東西。

In the last section, we saw that we can pass a second parameter to grammars with transforms: an accumulation variable or state that gets updated as your transform executes. There are times when your transforms will need to access auxiliary data that does not accumulate, so bundling it with the state parameter is impractical. Instead, you can pass auxiliary data as a third parameter, known as the data parameter. Below we show an example involving string processing where the data parameter is essential.
在上一節,我們看到了,我們可以傳遞第二個參數給帶有變換的語法:一個累計變量或狀態,它會在你的變換執行中被更新。有時,你的變換會需要訪問一些並非累計的輔助數據,所以以 state 參數來綁定它是不合適的。這時,你可以將這些輔助數據作為第三個參數來傳遞,即 data 參數。下面我們將示範一個字符串處理的例子,其中將用到 data 參數。

[Note] Note 備註

All Proto grammars are function objects that take one, two or three arguments: the expression, the state, and the data. There are no additional arguments to know about, we promise. In Haskell, there is set of tree traversal technologies known collectively as Scrap Your Boilerplate. In that framework, there are also three parameters: the term, the accumulator, and the context. These are Proto's expression, state and data parameters under different names.
所有Proto語法都是函數對象,它可能帶一個、兩個或三個參數:表達式、狀態和數據。我們承諾,沒有其它參數了。在Haskell中,有成套的樹遍歷技術,它們被統稱為 Scrap Your Boilerplate。在那個框架中,也有三個參數:術語、累計器和上下文。它們就是不同名字下的Proto表達式、狀態和數據參數。

Expression templates are often used as an optimization to eliminate temporary objects. Consider the problem of string concatenation: a series of concatenations would result in the needless creation of temporary strings. We can use Proto to make string concatenation very efficient. To make the problem more interesting, we can apply a locale-sensitive transformation to each character during the concatenation. The locale information will be passed as the data parameter.
表 達式模板通常被用作一種優化手段,以消除臨時對象。考慮一個字符串串接的問題:一系列的串接會導致不必要的臨時字符串的創建。我們可以用Proto來使得 字符串串接更為高效。為了讓這個問題更加有趣,我們可以在串接的過程中對每個字符施加一個locale敏感的變換。locale信息將作為data參數傳 遞。

Consider the following expression template:
考慮以下表達式模板:

proto::lit("hello") + " " + "world";

We would like to concatenate this string into a statically allocated wide character buffer, widening each character in turn using the specified locale. The first step is to write a grammar that describes this expression, with transforms that calculate the total string length. Here it is:
我們希望將這個字符串串接到一個靜態分配的寬字符緩衝區中,使用指定的locale對字符逐個擴寬。第一步是編寫一個語法來描述該表達式,並帶有計算字符串總長度的變換。如下:

// A grammar that matches string concatenation expressions, and
// a transform that calculates the total string length.
// 一個匹配字符串串接表達式的語法,以及一個計算字符串總長度的變換。
struct StringLength : proto::or_< proto::when< // When you find a character array ... 當你找到一個字符串數組...
proto::terminal<char[proto::N]> // ... the length is the size of the array minus 1. 數組長度減1。
, mpl::prior<mpl::sizeof_<proto::_value> >() > , proto::when< // The length of a concatenated string is ... 串接的字符串長度為...
proto::plus<StringLength, StringLength> // ... the sum of the lengths of each sub-string. 各個子串長度之和。
, proto::fold< _ , mpl::size_t<0>() , mpl::plus<StringLength, proto::_state>() > > > {};

Notice the use of proto::fold<>. It is a primitive transform that takes a sequence, a state, and function, just like std::accumulate(). The three template parameters are transforms. The first yields the sequence of expressions over which to fold, the second yields the initial state of the fold, and the third is the function to apply at each iteration. The use of proto::_ as the first parameter might have you confused. In addition to being Proto's wildcard, proto::_ is also a primitive transform that returns the current expression, which (if it is a non-terminal) is a sequence of its child expressions.
注意其中對 proto::fold<> 的使用。它是一個基本變換,接受一個序列、一個狀態和一個函數,就像 std::accumulate() 那樣。這三個模板參數都是變換。第一個參數產生要進行折疊的表達式序列,第二個參數產生折疊的初始狀態,第三個參數則是應用於每次迭代的函數。以 proto::_ 作為第一個參數可能會使你感到困惑。除了作為Proto的通配符以外,proto::_ 還是一個返回當前表達式的基本變換,即(如果它是一個非終結符)一個由各子表達式組成的序列。

Next, we need a function object that accepts a narrow string, a wide character buffer, and a std::ctype<> facet for doing the locale-specific stuff. It's fairly straightforward.
接著,我們需要一個函數對象,它接受一個窄字符串、一個寬字符緩衝區和一個 std::ctype<> facet 來進行特定locale的填充。它非常簡單。

// A function object that writes a narrow string
// into a wide buffer.
// 一個函數對象,將一個窄字符串寫入到寬字符緩衝區。
struct WidenCopy : proto::callable { typedef wchar_t *result_type; wchar_t * operator()(char const *str, wchar_t *buf, std::ctype<char> const &ct) const { for(; *str; ++str, ++buf) *buf = ct.widen(*str); return buf; } };

Finally, we need some transforms that actually walk the concatenated string expression, widens the characters and writes them to a buffer. We will pass a wchar_t* as the state parameter and update it as we go. We'll also pass the std::ctype<> facet as the data parameter. It looks like this:
最後,我們需要一些變換來實際遍歷要串接的字符串表達式,對字符進行擴寬,並將它們寫入至緩衝區。我們將傳入一個 wchar_t* 作為 state 參數,並隨著我們的進度更新它。我們還會傳入 std::ctype<> facet 作為 data 參數。它看起來像這樣:

// Write concatenated strings into a buffer, widening
// them as we go.
// 將串接的字符串寫入到緩衝區,並擴寬它們。
struct StringCopy : proto::or_< proto::when< proto::terminal<char[proto::N]> , WidenCopy(proto::_value, proto::_state, proto::_data) > , proto::when< proto::plus<StringCopy, StringCopy> , StringCopy( proto::_right , StringCopy(proto::_left, proto::_state, proto::_data) , proto::_data ) > > {};

Let's look more closely at the transform associated with non-terminals:
我們來仔細地看一下與非終結符相關聯的變換:

StringCopy(
    proto::_right
  , StringCopy(proto::_left, proto::_state, proto::_data)
  , proto::_data
)

This bears a resemblance to the transform in the previous section that folded an expression tree into a list. First we recurse on the left child, writing its strings into the wchar_t* passed in as the state parameter. That returns the new value of the wchar_t*, which is passed as state while transforming the right child. Both invocations receive the same std::ctype<>, which is passed in as the data parameter.
這與上一節中將表達式樹折疊為鏈表的那個變換非常相似。首先我們對左子節點進行遞歸,將它的字符串寫入到作為 state 參數傳入的 wchar_t* 中。返回的 wchar_t* 新值,在轉換右子節點時作為 state 傳入。兩個調用均接受同一個 std::ctype<>,它被作為 data 參數傳遞。

With these pieces in our pocket, we can implement our concatenate-and-widen function as follows:
通過我們口袋中的這些零件,我們可以實現我們的串接並擴寬,如下:

template<typename Expr>
void widen( Expr const &expr )
{
    // Make sure the expression conforms to our grammar 確保表達式符合我們的語法
BOOST_MPL_ASSERT(( proto::matches<Expr, StringLength> )); // Calculate the length of the string and allocate a buffer statically
// 計算字符串的總長度並靜態分配一個緩衝區
static std::size_t const length = boost::result_of<StringLength(Expr)>::type::value; wchar_t buffer[ length + 1 ] = {L'\0'}; // Get the current ctype facet 取得當前的ctype facet
std::locale loc; std::ctype<char> const &ct(std::use_facet<std::ctype<char> >(loc)); // Concatenate and widen the string expression 串接並擴寬這個字符串表達式
StringCopy()(expr, &buffer[0], ct); // Write out the buffer. 輸出該緩衝區。
std::wcout << buffer << std::endl; } int main() { widen( proto::lit("hello") + " " + "world" ); }

The above code displays:
以上代碼將顯示:

hello world

This is a rather round-about way of demonstrating that you can pass extra data to a transform as a third parameter. There are no restrictions on what this parameter can be, and (unlike the state parameter) Proto will never mess with it.
這是一個相當繞的示範方式,示範了你可以將額外的數據作為第三個參數進行傳遞。對於該參數可以是什麼,並沒有限制,而且(與 state 參數不同) Proto 絕對不會干預它。

Implicit Parameters to Primitive Transforms 給基本變換的隱式參數

Let's use the above example to illustrate some other niceties of Proto transforms. We've seen that grammars, when used as function objects, can accept up to 3 parameters, and that when using these grammars in callable transforms, you can also specify up to 3 parameters. Let's take another look at the transform associated with non-terminals above:
讓我們以上述例子為例,說明一下Proto變換的其它一些細節。我們已經看到,語法被作為函數對像使用時,可以接受多達3個參數,而且,把這些語法在可調用變換中使用時,你也可以指定最多3個參數。讓我們再看看上述例子中與非終結符相關聯的變換:

StringCopy(
    proto::_right
  , StringCopy(proto::_left, proto::_state, proto::_data)
  , proto::_data
)

Here we specify all three parameters to both invocations of the StringCopy grammar. But we don't have to specify all three. If we don't specify a third parameter, proto::_data is assumed. Likewise for the second parameter and proto::_state. So the above transform could have been written more simply as:
在此,我們對兩次 StringCopy 語法的調用都指定了所有三個參數。但是其實我們並不需要指定全部三個參數。如果我們不指定第三個參數,將會假定為 proto::_data。同樣,第二個參數會假定為 proto::_state。所以,以上變換可以簡化為:

StringCopy(
    proto::_right
  , StringCopy(proto::_left)
)

The same is true for any primitive transform. The following are all equivalent:
對於任意的基本變換,也是如此。以下寫法都是等價的:

Table 14.7. Implicit Parameters to Primitive Transforms
表 14.7. 基本變換的隱式參數

Equivalent Transforms 相互等價的變換

proto::when<_, StringCopy>

proto::when<_, StringCopy(_)>

proto::when<_, StringCopy(_, proto::_state)>

proto::when<_, StringCopy(_, proto::_state, proto::_data)>


[Note] Note 備註

Grammars Are Primitive Transforms Are Function Objects 語法是基本變換,也是函數對像

So far, we've said that all Proto grammars are function objects. But it's more accurate to say that Proto grammars are primitive transforms -- a special kind of function object that takes between 1 and 3 arguments, and that Proto knows to treat specially when used in a callable transform, as in the table above.
到目前為止,我們曾經說過,所有Proto語法都是函數對象。但是更準確的說法是,Proto語法是基本變換 -- 一種特定類型的函數對象,它接受1至3個參數,且Proto知道當語法被用在一個可調用變換中時,對其特殊看待,如上表所示。

[Note] Note 備註

Not All Function Objects Are Primitive Transforms 不是所有函數對象都是基本變換

You might be tempted now to drop the _state and _data parameters to WidenCopy(proto::_value, proto::_state, proto::_data). That would be an error. WidenCopy is just a plain function object, not a primitive transform, so you must specify all its arguments. We'll see later how to write your own primitive transforms.
也許現在你會想把 _state_data 參數從 WidenCopy(proto::_value, proto::_state, proto::_data) 中去掉。這樣做是錯誤的。WidenCopy 只是一個普通的函數對象,而不是一個基本變換,所以你必須指定所有參數。稍後我們將看到如何編寫你自己的基本變換。

Once you know that primitive transforms will always receive all three parameters -- expression, state, and data -- it makes things possible that wouldn't be otherwise. For instance, consider that for binary expressions, these two transforms are equivalent. Can you see why?
一旦你知道了基本變換總是接受所有三個參數 -- 表達式、狀態和數據 -- 這會讓某些意外的事情成為可能。例如,對於二元表達式,以下兩個變換是等價的。你能看出為什麼嗎?

Table 14.8. Two Equivalent Transforms
表 14.8. 兩個等價的變換

Without proto::fold<>

With proto::fold<>

StringCopy(
    proto::_right
  , StringCopy(proto::_left, proto::_state, proto::_data)
  , proto::_data
)

proto::fold<_, proto::_state, StringCopy>


Primitive transforms are the building blocks for more interesting composite transforms. Proto defines a bunch of generally useful primitive transforms. They are summarized below.
基本變換是有趣的複雜變換的構建塊。Proto定義了一組常用的基本變換。概述如下。

proto::_value

Given a terminal expression, return the value of the terminal.
給定一個終結符表達式,返回該終結符的值。

proto::_child_c<>

Given a non-terminal expression, proto::_child_c< N > returns the N -th child.
給定一個非終結符表達式,proto::_child_c< N > 返回第N個子節點。

proto::_child

A synonym for proto::_child_c<0>.
proto::_child_c<0> 的同義詞。

proto::_left

A synonym for proto::_child_c<0>.
proto::_child_c<0> 的同義詞。

proto::_right

A synonym for proto::_child_c<1>.
proto::_child_c<1> 的同義詞。

proto::_expr

Returns the current expression unmodified.
原樣返回當前表達式。

proto::_state

Returns the current state unmodified.
原樣返回當前狀態。

proto::_data

Returns the current data unmodified.
原樣返回當前數據。

proto::call<>

For a given callable transform CT , proto::call< CT > turns the callable transform into a primitive transform. This is useful for disambiguating callable transforms from object transforms, and also for working around compiler bugs with nested function types.
對於給定的可調用變換 CTproto::call< CT > 將該可調用變換轉為基本變換。這對於消除可調用變換與對像變換間的歧義非常有用,也可以繞過編譯器中關於嵌套函數類型的缺陷。

proto::make<>

For a given object transform OT , proto::make< OT > turns the object transform into a primitive transform. This is useful for disambiguating object transforms from callable transforms, and also for working around compiler bugs with nested function types.
對於給定的對象變換 OTproto::make< OT > 將該對像變換轉為基本變換。這對於消除對像變換與可調用變換間的歧義非常有用,也可以繞過編譯器中關於嵌套函數類型的缺陷。

proto::_default<>

Given a grammar G , proto::_default< G > evaluates the current node according to the standard C++ meaning of the operation the node represents. For instance, if the current node is a binary plus node, the two children will both be evaluated according to G and the results will be added and returned. The return type is deduced with the help of the Boost.Typeof library.
給定一個語法 Gproto::_default< G > 根據該節點所代表的操作的標準C++意義對當前節點進行求值。例如,如果當前節點為二元加法節點,則根據 G 對兩個子節點進行求值,然後將結果相加並返回。返回類型用 Boost.Typeof 庫來幫助推導。

proto::fold<>

Given three transforms ET , ST , and FT , proto::fold< ET , ST , FT > first evaluates ET to obtain a Fusion sequence and ST to obtain an initial state for the fold, and then evaluates FT for each element in the sequence to generate the next state from the previous.
給定三個變換 ET , ST , 和 FT , proto::fold< ET , ST , FT > 首先對 ET 求值得到一個Fusion序列,然後對 ST 求值得到一個用於折疊的初始狀態,最後對 FT 求值,對於序列中的每個元素,由上一個狀態生成下一個狀態。

proto::reverse_fold<>

Like proto::fold<>, except the elements in the Fusion sequence are iterated in reverse order.
proto::fold<> 類似,但是以相反順序遍歷Fusion序列中的元素。

proto::fold_tree<>

Like proto::fold< ET , ST , FT >, except that the result of the ET transform is treated as an expression tree that is flattened to generate the sequence to be folded. Flattening an expression tree causes child nodes with the same tag type as the parent to be put into sequence. For instance, a >> b >> c would be flattened to the sequence [a, b, c], and this is the sequence that would be folded.
類似於 proto::fold< ET , ST , FT >,但是 ET 變換的結果被視為一棵表達式樹,該樹被壓平後生成要折疊的序列。壓平一棵表達式樹將使得具有與父節點相同標籤類型的子節點被放入序列中。例如,a >> b >> c 會被壓平為序列 [a, b, c],該序列將被折疊。

proto::reverse_fold_tree<>

Like proto::fold_tree<>, except that the flattened sequence is iterated in reverse order.
類似於 proto::fold_tree<>,不過是以相反順序遍歷壓平的序列。

proto::lazy<>

A combination of proto::make<> and proto::call<> that is useful when the nature of the transform depends on the expression, state and/or data parameters. proto::lazy<R(A0,A1...An)> first evaluates proto::make<R()> to compute a callable type R2. Then, it evaluates proto::call<R2(A0,A1...An)>.
proto::make<>proto::call<> 的組合,當變換的本性是依賴於表達式、狀態和/或數據參數時使用。proto::lazy<R(A0,A1...An)> 首先對 proto::make<R()> 求值以計算一個可調用類型 R2。然後對 proto::call<R2(A0,A1...An)> 求值。

All Grammars Are Primitive Transforms 所有語法都是基本變換

In addition to the above primitive transforms, all of Proto's grammar elements are also primitive transforms. Their behaviors are described below.
除了以上的基本變換外,所有的Proto語法元素也都是基本變換。它們的行為描述如下。

proto::_

Return the current expression unmodified.
原樣返回當前表達式。

proto::or_<>

For the specified set of alternate sub-grammars, find the one that matches the given expression and apply its associated transform.
對於指定的候選子語法集,找出與給定表達式相匹配的候選項,並應用其相關聯的變換。

proto::and_<>

For the given set of sub-grammars, take the last sub-grammar and apply its associated transform.
對於給定的子語法集,取最後一個子語法,並應用其相關聯的變換。

proto::not_<>

Return the current expression unmodified.
原樣返回當前表達式。

proto::if_<>

Given three transforms, evaluate the first and treat the result as a compile-time Boolean value. If it is true, evaluate the second transform. Otherwise, evaluate the third.
給定三個變換,對第一個進行求值,將結果視為一個編譯期布爾值。如果該值為真,對第二個變換求值。否則對第三個變換求值。

proto::switch_<>

As with proto::or_<>, find the sub-grammar that matches the given expression and apply its associated transform.
類似於 proto::or_<>,找出與給定表達式相匹配的子語法,並應其相關聯的變換。

proto::terminal<>

Return the current terminal expression unmodified.
原樣返回當前的終結符表達式。

proto::plus<>, proto::nary_expr<>, et. al.

A Proto grammar that matches a non-terminal such as proto::plus< G0 , G1 >, when used as a primitive transform, creates a new plus node where the left child is transformed according to G0 and the right child with G1 .
匹配一個形如 proto::plus< G0 , G1 > 的非終結符的Proto語法,當被用作為一個基本變換時,創建一個新的加法節點,其中左子節點依據 G0 進行變換,而右子節點依據 G1 進行變換。

The Pass-Through Transform 直通變換

Note the primitive transform associated with grammar elements such as proto::plus<> described above. They possess a so-called pass-through transform. The pass-through transform accepts an expression of a certain tag type (say, proto::tag::plus) and creates a new expression of the same tag type, where each child expression is transformed according to the corresponding child grammar of the pass-through transform. So for instance this grammar ...
留意一下前面所介紹的與 proto::plus<> 這樣的語法元素相對應原基本變換。它們持有一個所謂的直通變換。直通變換接受某個標籤類型(如 proto::tag::plus)的表達式,並創建一個相同標籤類型的新表達式,其中的各個子表達式是由依照直通變換的相應子語法變換而來的。例如以下語法 ...

proto::function< X, proto::vararg<Y> >

... matches function expressions where the first child matches the X grammar and the rest match the Y grammar. When used as a transform, the above grammar will create a new function expression where the first child is transformed according to X and the rest are transformed according to Y
... 它匹配那些第一個子節點與 X 語法相匹配而其餘部分與 Y 語法相匹配的函數表達式。當上述語法被用作一個變換時,它將創建一個新的函數表達式,其第一個子節點是依照 X 變換而來的,剩餘部分則依照 Y 變換而來。

The following class templates in Proto can be used as grammars with pass-through transforms:
Proto中的以下類模板可以用作帶直通變換的語法:

Table 14.9. Class Templates With Pass-Through Transforms
表 14.9. 帶直通變換的類模板

Templates with Pass-Through Transforms 帶直通變換的模板

proto::unary_plus<>

proto::negate<>

proto::dereference<>

proto::complement<>

proto::address_of<>

proto::logical_not<>

proto::pre_inc<>

proto::pre_dec<>

proto::post_inc<>

proto::post_dec<>

proto::shift_left<>

proto::shift_right<>

proto::multiplies<>

proto::divides<>

proto::modulus<>

proto::plus<>

proto::minus<>

proto::less<>

proto::greater<>

proto::less_equal<>

proto::greater_equal<>

proto::equal_to<>

proto::not_equal_to<>

proto::logical_or<>

proto::logical_and<>

proto::bitwise_and<>

proto::bitwise_or<>

proto::bitwise_xor<>

proto::comma<>

proto::mem_ptr<>

proto::assign<>

proto::shift_left_assign<>

proto::shift_right_assign<>

proto::multiplies_assign<>

proto::divides_assign<>

proto::modulus_assign<>

proto::plus_assign<>

proto::minus_assign<>

proto::bitwise_and_assign<>

proto::bitwise_or_assign<>

proto::bitwise_xor_assign<>

proto::subscript<>

proto::if_else_<>

proto::function<>

proto::unary_expr<>

proto::binary_expr<>

proto::nary_expr<>


The Many Roles of Proto Operator Metafunctions  Proto操作符元函數的多重角色

We've seen templates such as proto::terminal<>, proto::plus<> and proto::nary_expr<> fill many roles. They are metafunction that generate expression types. They are grammars that match expression types. And they are primitive transforms. The following code samples show examples of each.
我們已經看到,像 proto::terminal<>, proto::plus<>proto::nary_expr<> 這些模板充當了多重角色。它們是生成表達式類型的元函數。它們是匹配表達式類型的語法。它們也是基本變換。以下代碼展現了它們的各種例子。

As Metafunctions ... 作為元函數...

// proto::terminal<> and proto::plus<> are metafunctions
// that generate expression types:
// proto::terminal<>和proto::plus<>是生成表達式類型的元函數:
typedef proto::terminal<int>::type int_; typedef proto::plus<int_, int_>::type plus_; int_ i = {42}, j = {24}; plus_ p = {i, j};

As Grammars ... 作為語法...

// proto::terminal<> and proto::plus<> are grammars that
// match expression types
// proto::terminal<>和proto::plus<>是匹配表達式類型的語法
struct Int : proto::terminal<int> {}; struct Plus : proto::plus<Int, Int> {}; BOOST_MPL_ASSERT(( proto::matches< int_, Int > )); BOOST_MPL_ASSERT(( proto::matches< plus_, Plus > ));

As Primitive Transforms ... 作為基本變換...

// A transform that removes all unary_plus nodes in an expression
// 一個將表達式中的所有unary_plus節點移除的變換
struct RemoveUnaryPlus : proto::or_< proto::when< proto::unary_plus<RemoveUnaryPlus> , RemoveUnaryPlus(proto::_child) > // Use proto::terminal<> and proto::nary_expr<>
// both as grammars and as primitive transforms.
// 把proto::terminal<>和proto::nary_expr<>同時用作語法和基本變換。
, proto::terminal<_> , proto::nary_expr<_, proto::vararg<RemoveUnaryPlus> > > {}; int main() { proto::literal<int> i(0); proto::display_expr(
+i - +(i - +i) ); proto::display_expr(
RemoveUnaryPlus()( +i - +(i - +i) ) ); }

The above code displays the following, which shows that unary plus nodes have been stripped from the expression:
以上代碼輸出如下,可以看到單參數加法已經從表達式中去掉了:

minus(
unary_plus(
terminal(0)
)
, unary_plus(
minus(
terminal(0)
, unary_plus(
terminal(0)
)
)
)
)
minus(
terminal(0)
, minus(
terminal(0)
, terminal(0)
)
)

In previous sections, we've seen how to compose larger transforms out of smaller transforms using function types. The smaller transforms from which larger transforms are composed are primitive transforms, and Proto provides a bunch of common ones such as _child0 and _value. In this section we'll see how to author your own primitive transforms.
在上一節中,我們看到了用函數類型從較小的變換組合出較大的變換。組成較大變換的較小變換是基本變換,Proto提供了一組常用的基本變換,如 _child0_value。在本節中,我們將看看如何創作你自己的基本變換。

[Note] Note 備註

There are a few reasons why you might want to write your own primitive transforms. For instance, your transform may be complicated, and composing it out of primitives becomes unwieldy. You might also need to work around compiler bugs on legacy compilers that make composing transforms using function types problematic. Finally, you might also decide to define your own primitive transforms to improve compile times. Since Proto can simply invoke a primitive transform directly without having to process arguments or differentiate callable transforms from object transforms, primitive transforms are more efficient.

為 什麼你可能會想編寫自己的基本變換呢?這有幾個原因。例如,你的變換可能很複雜,由基本變換組合出來會很笨重。你也可能需要繞過一些舊編譯器中的缺陷,如 用函數類型來組合變換時存在問題。最後,你還可能決定定義你自己的基本變換來改進編譯時間。由於Proto可以直接調用一個基本變換,而無須處理參數或區 分可調用變換與對像變換,所以基本變換會更為高效。

Primitive transforms inherit from proto::transform<> and have a nested impl<> template that inherits from proto::transform_impl<>. For example, this is how Proto defines the _child_c< N > transform, which returns the N -th child of the current expression:
基本變換繼承自 proto::transform<> 且帶有一個嵌套的 impl<> 模板,該模板繼承自 proto::transform_impl<>。例如,以下是Proto定義 _child_c< N > 變換的方法,該變換返回當前表達式的第N個子節點。

namespace boost { namespace proto
{
    // A primitive transform that returns N-th child
// of the current expression.
// 返回當前表達式的第N個子節點的基本變換。
template<int N> struct _child_c : transform<_child_c<N> > { template<typename Expr, typename State, typename Data> struct impl : transform_impl<Expr, State, Data> { typedef typename result_of::child_c<Expr, N>::type result_type; result_type operator ()( typename impl::expr_param expr , typename impl::state_param state , typename impl::data_param data ) const { return proto::child_c<N>(expr); } }; }; // Note that _child_c<N> is callable, so that
// it can be used in callable transforms, as:
// _child_c<0>(_child_c<1>)
// 注意,_child_c<N>是可調用的,這樣它就可以在可調用
// 變換中使用,如:_child_c<0>(_child_c<1>)
template<int N> struct is_callable<_child_c<N> > : mpl::true_ {}; }}

The proto::transform<> base class provides the operator() overloads and the nested result<> template that make your transform a valid function object. These are implemented in terms of the nested impl<> template you define.
基類 proto::transform<> 提供了 operator() 重載和嵌套的 result<> 模板,使你的變換成為一個有效的函數對象。它們是依據你定義的嵌套 impl<> 模板來實現的。

The proto::transform_impl<> base class is a convenience. It provides some nested typedefs that are generally useful. They are specified in the table below:
基類 proto::transform_impl<> 是一個便利的工具。它提供了一些常用的嵌套typedef。在下表中列出:

Table 14.10. proto::transform_impl<Expr, State, Data> typedefs
表 14.10. proto::transform_impl<Expr, State, Data> 的 typedefs

typedef

Equivalent To 等價於

expr

typename remove_reference<Expr>::type

state

typename remove_reference<State>::type

data

typename remove_reference<Data>::type

expr_param

typename add_reference<typename add_const<Expr>::type>::type

state_param

typename add_reference<typename add_const<State>::type>::type

data_param

typename add_reference<typename add_const<Data>::type>::type


You'll notice that _child_c::impl::operator() takes arguments of types expr_param, state_param, and data_param. The typedefs make it easy to accept arguments by reference or const reference accordingly.
你應該會留意到 _child_c::impl::operator() 所接受的參數類型分別是 expr_param, state_param, 和 data_param。這些 typedef 使得我們可以很容易地通過引用或常量引用來接受參數。

The only other interesting bit is the is_callable<> specialization, which will be described in the next section.
現在,有趣的部分只剩下 is_callable<> 的特化了,它將在下一節中介紹。

Transforms are typically of the form proto::when< Something, R(A0,A1,...) >. The question is whether R represents a function to call or an object to construct, and the answer determines how proto::when<> evaluates the transform. proto::when<> uses the proto::is_callable<> trait to disambiguate between the two. Proto does its best to guess whether a type is callable or not, but it doesn't always get it right. It's best to know the rules Proto uses, so that you know when you need to be more explicit.
變換典型地具有形如 proto::when< Something, R(A0,A1,...) > 的格式。問題是,R 代表的是一個要調用的函數,抑或是一個要構造的對象,其答案取決於 proto::when<> 如何對該變換進行求值。proto::when<> 使用 proto::is_callable<> trait 來區分這兩者。Proto盡最大努力去猜測一個類型是否可調用,但是它不會總是猜對的。最好還是知道一下Proto所使用的規則,這樣你就知道何時需要做得更清楚些。

For most types R, proto::is_callable<R> checks for inheritance from proto::callable. However, if the type R is a template specialization, Proto assumes that it is not callable even if the template inherits from proto::callable. We'll see why in a minute. Consider the following erroneous callable object:
對於大多數類型 R, proto::is_callable<R> 檢查它是否繼承自 proto::callable。不過,如果類型 R 是一個模板特化,Proto將會假定它不是可調用的,即使該模板繼承自 proto::callable。等會我們將看到為什麼會這樣。考慮以下這個錯誤的可調用對像:

// Proto can't tell this defines something callable!
// Proto不能將這個定義認作為可調用的!
template<typename T> struct times2 : proto::callable { typedef T result_type; T operator()(T i) const { return i * 2; } }; // ERROR! This is not going to multiply the int by 2:
// 錯誤!這不會把int乘以2:
struct IntTimes2 : proto::when< proto::terminal<int> , times2<int>(proto::_value) > {};

The problem is that Proto doesn't know that times2<int> is callable, so rather that invoking the times2<int> function object, Proto will try to construct a times2<int> object and initialize it will an int. That will not compile.
問題在於,Proto不知道 times2<int> 是可調用的,所以不會調用 times2<int> 函數對象,Proto會嘗試構造一個 times2<int> 對象並將它初始化為 int。這樣將不能編譯。

[Note] Note 備註

Why can't Proto tell that times2<int> is callable? After all, it inherits from proto::callable, and that is detectable, right? The problem is that merely asking whether some type X<Y> inherits from callable will cause the template X<Y> to be instantiated. That's a problem for a type like std::vector<_value(_child1)>. std::vector<> will not suffer to be instantiated with _value(_child1) as a template parameter. Since merely asking the question will sometimes result in a hard error, Proto can't ask; it has to assume that X<Y> represents an object to construct and not a function to call.
為什麼Proto不能認為 times2<int> 是可調用的呢?畢竟,它是繼承自 proto::callable 的,而且這是可以被檢測到的,不是嗎?問題是,哪怕只是詢問一下 type X<Y> 是否繼承自 callable,也會引起模板 X<Y> 的實例化。這對於象 std::vector<_value(_child1)> 這樣的類型來說就是一個問題。std::vector<> 不能忍受以 _value(_child1) 作為模板參數進行實例化。因為僅僅是詢問一下這個問題,有時都會導致一個硬錯誤,所以Proto不能問;它必須假定 X<Y> 代表的是一個要構造的對象,而不是一個要調用的函數。

There are a couple of solutions to the times2<int> problem. One solution is to wrap the transform in proto::call<>. This forces Proto to treat times2<int> as callable:
有幾種方法來解決這個 times2<int> 的問題。一種方法是,把這個變換包裝在 proto::call<> 中。這將強迫Proto把 times2<int> 視為可調用的:

// OK, calls times2<int>  好的,可以調用times2<int>
struct IntTimes2 : proto::when< proto::terminal<int> , proto::call<times2<int>(proto::_value)> > {};

This can be a bit of a pain, because we need to wrap every use of times2<int>, which can be tedious and error prone, and makes our grammar cluttered and harder to read.
這可能有一點麻煩,因為我們需要對 times2<int> 的每一次使用都進行包裝,這是很乏味且易錯的,也使得我們的語法顯得混亂而更難閱讀。

Another solution is to specialize proto::is_callable<> on our times2<> template:
另一個方法是,針對我們的 times2<> 模板特化 proto::is_callable<>

namespace boost { namespace proto
{
    // Tell Proto that times2<> is callable 告訴Proto,times2<>是可調用的
template<typename T> struct is_callable<times2<T> > : mpl::true_ {}; }} // OK, times2<> is callable 好的,times2<>是可調用的
struct IntTimes2 : proto::when< proto::terminal<int> , times2<int>(proto::_value) > {};

This is better, but still a pain because of the need to open Proto's namespace.
這個方法更好些,但還是有點麻煩,因為需要打開Proto的名字空間。

You could simply make sure that the callable type is not a template specialization. Consider the following:
你可以只需確保可調用類型不是一個模板特化。如下:

// No longer a template specialization!  不再是一個模板特化!
struct times2int : times2<int> {}; // OK, times2int is callable 好的,times2int是可調用的
struct IntTimes2 : proto::when< proto::terminal<int> , times2int(proto::_value) > {};

This works because now Proto can tell that times2int inherits (indirectly) from proto::callable. Any non-template types can be safely checked for inheritance because, as they are not templates, there is no worry about instantiation errors.
這樣可以用了,因為現在Proto可以認出 times2int (間接地)繼承自 proto::callable。任何非模板類型都可以安全地檢查繼承關係,因為它們不是模板,不需要擔心實例化的錯誤。

There is one last way to tell Proto that times2<> is callable. You could add an extra dummy template parameter that defaults to proto::callable:
以下是最後一種告知Proto times2<> 可調用的方法。你可以增加一個額外的啞模板參數,該參數的缺省值為 proto::callable

// Proto will recognize this as callable  Proto將此模板識別為可調用的
template<typename T, typename Callable = proto::callable> struct times2 : proto::callable { typedef T result_type; T operator()(T i) const { return i * 2; } }; // OK, this works! 好的,可以工作!
struct IntTimes2 : proto::when< proto::terminal<int> , times2<int>(proto::_value) > {};

Note that in addition to the extra template parameter, times2<> still inherits from proto::callable. That's not necessary in this example but it is good style because any types derived from times2<> (as times2int defined above) will still be considered callable.
注意,增加了這個額外的模板參數後,times2<> 依然繼承自 proto::callable。在這個例子中,這是不必要的,但這是一個好的習慣,因為派生自 times2<> 的任意類型(如前面的 times2int)也還會被認為是可調用的。

A code example is worth a thousand words ...
一個代碼示例抵得上千言萬語...

A trivial example which builds and expression template and evaluates it.
一個簡單的例子,構建一個表達式模板並對它進行求值。

////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <iostream> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> #include <boost/typeof/std/ostream.hpp> namespace proto = boost::proto; proto::terminal< std::ostream & >::type cout_ = {std::cout}; template< typename Expr > void evaluate( Expr const & expr ) { proto::default_context ctx; proto::eval(expr, ctx); } int main() { evaluate( cout_ << "hello" << ',' << " world" ); return 0; }

A simple example that builds a miniature domain-specific embedded language for lazy arithmetic expressions, with TR1 bind-style argument placeholders.
一個簡單的例子,構建一個用於惰性算術表達式的微型領域專用嵌入式語言,使用TR1 bind風格的參數佔位符。

//  Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This is a simple example of how to build an arithmetic expression
// evaluator with placeholders.
// 這是一個簡單的例子,關於如何構建一個帶佔位符的算術表達式求值器。
#include <iostream> #include <boost/mpl/int.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; template<int I> struct placeholder {}; // Define some placeholders
proto::terminal< placeholder< 1 > >::type const _1 = {{}}; proto::terminal< placeholder< 2 > >::type const _2 = {{}}; // Define a calculator context, for evaluating arithmetic expressions
// 定義一個計算器上下文,用於對算術表達式進行求值
struct calculator_context : proto::callable_context< calculator_context const > { // The values bound to the placeholders 綁定至佔位符的值
double d[2]; // The result of evaluating arithmetic expressions 算術表達式的求值結果
typedef double result_type; explicit calculator_context(double d1 = 0., double d2 = 0.) { d[0] = d1; d[1] = d2; } // Handle the evaluation of the placeholder terminals 處理對佔位符終結符的求值
template<int I> double operator ()(proto::tag::terminal, placeholder<I>) const { return d[ I - 1 ]; } }; template<typename Expr> double evaluate( Expr const &expr, double d1 = 0., double d2 = 0. ) { // Create a calculator context with d1 and d2 substituted for _1 and _2
// 創建一個計算器上下文,以d1和d2替代_1和_2
calculator_context const ctx(d1, d2); // Evaluate the calculator expression with the calculator_context
// 以calculator_context對計算器表達式進行求值
return proto::eval(expr, ctx); } int main() { // Displays "5"
std::cout << evaluate( _1 + 2.0, 3.0 ) << std::endl; // Displays "6"
std::cout << evaluate( _1 * _2, 3.0, 2.0 ) << std::endl; // Displays "0.5"
std::cout << evaluate( (_1 - _2) / _2, 3.0, 2.0 ) << std::endl; return 0; }

An extension of the Calc1 example that uses proto::extends<> to make calculator expressions valid function objects that can be used with STL algorithms.
對Calc1例子的擴展,使用 proto::extends<> 來讓計算器表達式的有效函數對象可以用於STL算法。

//  Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This example enhances the simple arithmetic expression evaluator
// in calc1.cpp by using proto::extends to make arithmetic
// expressions immediately evaluable with operator (), a-la a
// function object
// 這個例子增強了在calc1.cpp中的簡單算術表達式計算器,通過使用proto::extends
// 來讓算術表達式可以按函數對象的方式用operator()進行立即求值
#include <iostream> #include <boost/mpl/int.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // Will be used to define the placeholders _1 and _2 用於定義佔位符_1和_2
template<int I> struct placeholder {}; // For expressions in the calculator domain, operator ()
// will be special; it will evaluate the expression.
// 對於計算器領域中的表達式,operator()將被特殊化;它會對表達式進行求值。
struct calculator_domain; // Define a calculator context, for evaluating arithmetic expressions
// (This is as before, in calc1.cpp)
// 定義一個計算器上下文,用於對算術表達式進行求值(和前面calc1.cpp一樣)
struct calculator_context : proto::callable_context< calculator_context const > { // The values bound to the placeholders 綁定到佔位符的值
double d[2]; // The result of evaluating arithmetic expressions 算術表達式的求值結果
typedef double result_type; explicit calculator_context(double d1 = 0., double d2 = 0.) { d[0] = d1; d[1] = d2; } // Handle the evaluation of the placeholder terminals 處理佔位符終結符的求值
template<int I> double operator ()(proto::tag::terminal, placeholder<I>) const { return d[ I - 1 ]; } }; // Wrap all calculator expressions in this type, which defines
// operator () to evaluate the expression.
// 將所有計算器表達式包入此類型中,它定義了operator()來對表達式進行求值。
template<typename Expr> struct calculator_expression : proto::extends<Expr, calculator_expression<Expr>, calculator_domain> { typedef proto::extends<Expr, calculator_expression<Expr>, calculator_domain> base_type; explicit calculator_expression(Expr const &expr = Expr()) : base_type(expr) {} using base_type::operator =; // Override operator () to evaluate the expression
// 重載operator()來對表達式求值
double operator ()() const { calculator_context const ctx; return proto::eval(*this, ctx); } double operator ()(double d1) const { calculator_context const ctx(d1); return proto::eval(*this, ctx); } double operator ()(double d1, double d2) const { calculator_context const ctx(d1, d2); return proto::eval(*this, ctx); } }; // Tell proto how to generate expressions in the calculator_domain
// 告知proto如何生成calculator_domain中的表達式
struct calculator_domain : proto::domain<proto::generator<calculator_expression> > {}; // Define some placeholders (notice they're wrapped in calculator_expression<>)
// 定義一些佔位符(注意,它們被包在calculator_expression<>中)
calculator_expression<proto::terminal< placeholder< 1 > >::type> const _1; calculator_expression<proto::terminal< placeholder< 2 > >::type> const _2; // Now, our arithmetic expressions are immediately executable function objects:
// 現在,我們的算術表達式變成了可以立即執行的函數對像:
int main() { // Displays "5"
std::cout << (_1 + 2.0)( 3.0 ) << std::endl; // Displays "6"
std::cout << ( _1 * _2 )( 3.0, 2.0 ) << std::endl; // Displays "0.5"
std::cout << ( (_1 - _2) / _2 )( 3.0, 2.0 ) << std::endl; return 0; }

An extension of the Calc2 example that uses a Proto transform to calculate the arity of a calculator expression and statically assert that the correct number of arguments are passed.
Calc2例子的一個擴展,使用一個Proto變換來計算一個計算器表達式的arity,並靜態地對傳入的參數數量進行斷言。

//  Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This example enhances the arithmetic expression evaluator
// in calc2.cpp by using a proto transform to calculate the
// number of arguments an expression requires and using a
// compile-time assert to guarantee that the right number of
// arguments are actually specified.
// 這個例子增強了calc2.cpp中的算術表達式求值器,通過使用一個proto變換來計算一個
// 表達式所要求的參數數量,並使用一個編譯期斷言來保證實際給定的參數數量是正確的。
#include <iostream> #include <boost/mpl/int.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/min_max.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> #include <boost/proto/transform.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // Will be used to define the placeholders _1 and _2 用於定義佔位符_1和_2
template<typename I> struct placeholder : I {}; // This grammar basically says that a calculator expression is one of:
// - A placeholder terminal
// - Some other terminal
// - Some non-terminal whose children are calculator expressions
// In addition, it has transforms that say how to calculate the
// expression arity for each of the three cases.
// 這個語法基本上是說,一個計算器表達式應為以下之一:
// - 一個佔位符終結符
// - 其它終結符
// - 非終結符,其子節點為計算器表達式
// 另外,它帶有一些變換,分別說明了在這三種情況下如何計算表達式的arity。
struct CalculatorGrammar : proto::or_< // placeholders have a non-zero arity ... 佔位符具有非0的arity...
proto::when< proto::terminal< placeholder<_> >, proto::_value > // Any other terminals have arity 0 ... 其它終結符的arity為0
, proto::when< proto::terminal<_>, mpl::int_<0>() > // For any non-terminals, find the arity of the children and
// take the maximum. This is recursive.
// 對於任意的非終結符,找出各個子節點的arity並計算其中的最大值。這是遞歸的。
, proto::when< proto::nary_expr<_, proto::vararg<_> > , proto::fold<_, mpl::int_<0>(), mpl::max<CalculatorGrammar, proto::_state>() > > > {}; // Simple wrapper for calculating a calculator expression's arity.
// It specifies mpl::int_<0> as the initial state. The data, which
// is not used, is mpl::void_.
// 一個簡單的包裝器,用於計算一個計算器表達式的arity。它指定mpl::int_<0>為
// 初始狀態。數據參數未使用,為mpl::void_。
template<typename Expr> struct calculator_arity : boost::result_of<CalculatorGrammar(Expr, mpl::int_<0>, mpl::void_)> {}; // For expressions in the calculator domain, operator ()
// will be special; it will evaluate the expression.
// 對於計算器領域中的表達式,operator()被特殊化;它將對表達式進行求值。
struct calculator_domain; // Define a calculator context, for evaluating arithmetic expressions
// (This is as before, in calc1.cpp and calc2.cpp)
// 定義一個計算器上下文,用於對算術表達式求值(與calc1.cpp和calc2.cpp中的一樣)
struct calculator_context : proto::callable_context< calculator_context const > { // The values bound to the placeholders 綁定至佔位符的值
double d[2]; // The result of evaluating arithmetic expressions 算術表達式的求值結果
typedef double result_type; explicit calculator_context(double d1 = 0., double d2 = 0.) { d[0] = d1; d[1] = d2; } // Handle the evaluation of the placeholder terminals 處理對佔位符終結符的求值
template<typename I> double operator ()(proto::tag::terminal, placeholder<I>) const { return d[ I() - 1 ]; } }; // Wrap all calculator expressions in this type, which defines
// operator () to evaluate the expression.
// 將所有計算器表達式包入此類型,它定義了operator()來對表達式進行求值
template<typename Expr> struct calculator_expression : proto::extends<Expr, calculator_expression<Expr>, calculator_domain> { typedef proto::extends<Expr, calculator_expression<Expr>, calculator_domain> base_type; explicit calculator_expression(Expr const &expr = Expr()) : base_type(expr) {} using base_type::operator =; // Override operator () to evaluate the expression
// 重載operator()來對表達式進行求值
double operator ()() const { // Assert that the expression has arity 0
BOOST_MPL_ASSERT_RELATION(0, ==, calculator_arity<Expr>::type::value); calculator_context const ctx; return proto::eval(*this, ctx); } double operator ()(double d1) const { // Assert that the expression has arity 1 斷言該表達式的arity為1
BOOST_MPL_ASSERT_RELATION(1, ==, calculator_arity<Expr>::type::value); calculator_context const ctx(d1); return proto::eval(*this, ctx); } double operator ()(double d1, double d2) const { // Assert that the expression has arity 2 斷言該表達式的arity為2
BOOST_MPL_ASSERT_RELATION(2, ==, calculator_arity<Expr>::type::value); calculator_context const ctx(d1, d2); return proto::eval(*this, ctx); } }; // Tell proto how to generate expressions in the calculator_domain
// 告知proto如何生成calculator_domain中的表達式
struct calculator_domain : proto::domain<proto::generator<calculator_expression> > {}; // Define some placeholders (notice they're wrapped in calculator_expression<>)
// 定義一些佔位符(注意,它們被包裝在calculator_expression<>中)
calculator_expression<proto::terminal< placeholder< mpl::int_<1> > >::type> const _1; calculator_expression<proto::terminal< placeholder< mpl::int_<2> > >::type> const _2; // Now, our arithmetic expressions are immediately executable function objects:
// 現在,我們的算術表達式變成了可以立即執行的函數對像:
int main() { // Displays "5"
std::cout << (_1 + 2.0)( 3.0 ) << std::endl; // Displays "6"
std::cout << ( _1 * _2 )( 3.0, 2.0 ) << std::endl; // Displays "0.5"
std::cout << ( (_1 - _2) / _2 )( 3.0, 2.0 ) << std::endl; // This won't compile because the arity of the
// expression doesn't match the number of arguments
// 以下不能編譯,因為該表達式的arity與參數數量不匹配
// ( (_1 - _2) / _2 )( 3.0 );
return 0; }

This example constructs a mini-library for linear algebra, using expression templates to eliminate the need for temporaries when adding vectors of numbers.
這個例子構建一個線性代數的小型庫,使用了表達式模板來消除數字向量相加時的臨時對象。

This example uses a domain with a grammar to prune the set of overloaded operators. Only those operators that produce valid lazy vector expressions are allowed.
這個例子使用了一個帶語法的領域來減少操作符的重載。只有那些可以生成有效的惰性向量表達式的操作符被允許。

///////////////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This example constructs a mini-library for linear algebra, using
// expression templates to eliminate the need for temporaries when
// adding vectors of numbers.
// 這個例子構建一個線性代數的小型庫,使用了表達式模板來消除數字向量相加時的臨時對象。
//
// This example uses a domain with a grammar to prune the set
// of overloaded operators. Only those operators that produce
// valid lazy vector expressions are allowed.
// 這個例子使用了一個帶語法的領域來減少操作符的重載。只有那些可以生成有
// 有效的惰性向量表達式的操作符被允許。
#include <vector> #include <iostream> #include <boost/mpl/int.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // This grammar describes which lazy vector expressions
// are allowed; namely, vector terminals and addition
// and subtraction of lazy vector expressions.
// 這個語法描述了哪些惰性向量表達式可以使用;即只允許向量終結符
// 以及惰性向量表達式的加法和減法。
struct LazyVectorGrammar : proto::or_< proto::terminal< std::vector<_> > , proto::plus< LazyVectorGrammar, LazyVectorGrammar > , proto::minus< LazyVectorGrammar, LazyVectorGrammar > > {}; // Expressions in the lazy vector domain must conform
// to the lazy vector grammar
// 在惰性向量領域中的表達式必須符合惰性向量語法
struct lazy_vector_domain; // Here is an evaluation context that indexes into a lazy vector
// expression, and combines the result.
// 這是一個求值上下文,對惰性向量表達式進行索引並組合出結果。
template<typename Size = std::size_t> struct lazy_subscript_context { lazy_subscript_context(Size subscript) : subscript_(subscript) {} // Use default_eval for all the operations ...
// 對所有操作使用default_eval ...
template<typename Expr, typename Tag = typename Expr::proto_tag> struct eval : proto::default_eval<Expr, lazy_subscript_context> {}; // ... except for terminals, which we index with our subscript
// ... 除了終結符,對於終結符我們用下標進行索引
template<typename Expr> struct eval<Expr, proto::tag::terminal> { typedef typename proto::result_of::value<Expr>::type::value_type result_type; result_type operator ()( Expr const & expr, lazy_subscript_context & ctx ) const { return proto::value( expr )[ ctx.subscript_ ]; } }; Size subscript_; }; // Here is the domain-specific expression wrapper, which overrides
// operator [] to evaluate the expression using the lazy_subscript_context.
// 這是領域專用表達式的包裝器,它重載了operator[],使用lazy_subscript_context來對表達式求值。
template<typename Expr> struct lazy_vector_expr : proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain> { typedef proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain> base_type; lazy_vector_expr( Expr const & expr = Expr() ) : base_type( expr ) {} // Use the lazy_subscript_context<> to implement subscripting
// of a lazy vector expression tree.
// 使用lazy_subscript_context<>來實現一個惰性向量表達式樹的下標操作。
template< typename Size > typename proto::result_of::eval< Expr, lazy_subscript_context<Size> >::type operator []( Size subscript ) const { lazy_subscript_context<Size> ctx(subscript); return proto::eval(*this, ctx); } }; // Here is our lazy_vector terminal, implemented in terms of lazy_vector_expr
// 這是我們的lazy_vector終結符,依照lazy_vector_expr實現
template< typename T > struct lazy_vector : lazy_vector_expr< typename proto::terminal< std::vector<T> >::type > { typedef typename proto::terminal< std::vector<T> >::type expr_type; lazy_vector( std::size_t size = 0, T const & value = T() ) : lazy_vector_expr<expr_type>( expr_type::make( std::vector<T>( size, value ) ) ) {} // Here we define a += operator for lazy vector terminals that
// takes a lazy vector expression and indexes it. expr[i] here
// uses lazy_subscript_context<> under the covers.
// 這裡我們為惰性向量終結符定義一個+=操作符,接受一個惰性向量表達式並對之
// 進行索引。這裡的expr[i]在其內部使用了lazy_subscript_context<>。
template< typename Expr > lazy_vector &operator += (Expr const & expr) { std::size_t size = proto::value(*this).size(); for(std::size_t i = 0; i < size; ++i) { proto::value(*this)[i] += expr[i]; } return *this; } }; // Tell proto that in the lazy_vector_domain, all
// expressions should be wrapped in lazy_vector_expr<>
// 告知proto,在lazy_vector_domain中,所有表達式都應包入lazy_vector_expr<>
struct lazy_vector_domain : proto::domain<proto::generator<lazy_vector_expr>, LazyVectorGrammar> {}; int main() { // lazy_vectors with 4 elements each. 各帶4個元素的lazy_vector
lazy_vector< double > v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 ); // Add two vectors lazily and get the 2nd element.
// 將兩個向量惰性相加並取出第2個元素。
double d1 = ( v2 + v3 )[ 2 ]; // Look ma, no temporaries! 看,沒有臨時對象!
std::cout << d1 << std::endl; // Subtract two vectors and add the result to a third vector.
// 將兩個向量相減並將結果加到第三個向量上。
v1 += v2 - v3; // Still no temporaries! 依然沒有臨時對象!
std::cout << '{' << v1[0] << ',' << v1[1] << ',' << v1[2] << ',' << v1[3] << '}' << std::endl; // This expression is disallowed because it does not conform
// to the LazyVectorGrammar
// 這個表達式是不允許的,因為它不符合LazyVectorGrammar
//(v2 + v3) += v1;
return 0; }

This is a simple example of doing arbitrary type manipulations with Proto transforms. It takes some expression involving primary colors and combines the colors according to arbitrary rules. It is a port of the RGB example from PETE.
這是一個簡單的例子,以Proto變換進行任意的類型操作。它接受帶有三原色的表達式,並根據任意規則組合成顏色。它是對 PETE 中的RGB例子的一個移植。

///////////////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This is a simple example of doing arbitrary type manipulations with proto
// transforms. It takes some expression involving primary colors and combines
// the colors according to arbitrary rules. It is a port of the RGB example
// from PETE (http://www.codesourcery.com/pooma/download.html).
// 這是一個簡單的例子,以Proto變換進行任意的類型操作。它接受帶有三原色的表達式,並根據任意規則進行
// 顏色組合。這是對PETE(http://www.codesourcery.com/pooma/download.html)中的RGB例子的移植。
#include <iostream> #include <boost/proto/core.hpp> #include <boost/proto/transform.hpp> namespace proto = boost::proto; struct RedTag { friend std::ostream &operator <<(std::ostream &sout, RedTag) { return sout << "This expression is red."; } }; struct BlueTag { friend std::ostream &operator <<(std::ostream &sout, BlueTag) { return sout << "This expression is blue."; } }; struct GreenTag { friend std::ostream &operator <<(std::ostream &sout, GreenTag) { return sout << "This expression is green."; } }; typedef proto::terminal<RedTag>::type RedT; typedef proto::terminal<BlueTag>::type BlueT; typedef proto::terminal<GreenTag>::type GreenT; struct Red; struct Blue; struct Green; ///////////////////////////////////////////////////////////////////////////////
// A transform that produces new colors according to some arbitrary rules:
// red & green give blue, red & blue give green, blue and green give red.
// 一個根據某些任意規則生成新顏色的變換:紅和綠生成藍,紅和藍生成綠,藍和綠生成紅。
struct Red : proto::or_< proto::plus<Green, Blue> , proto::plus<Blue, Green> , proto::plus<Red, Red> , proto::terminal<RedTag> > {}; struct Green : proto::or_< proto::plus<Red, Blue> , proto::plus<Blue, Red> , proto::plus<Green, Green> , proto::terminal<GreenTag> > {}; struct Blue : proto::or_< proto::plus<Red, Green> , proto::plus<Green, Red> , proto::plus<Blue, Blue> , proto::terminal<BlueTag> > {}; struct RGB : proto::or_< proto::when< Red, RedTag() > , proto::when< Blue, BlueTag() > , proto::when< Green, GreenTag() > > {}; template<typename Expr> void printColor(Expr const & expr) { int i = 0; // dummy state and data parameter, not used 狀態和數據參數的啞元,不使用
std::cout << RGB()(expr, i, i) << std::endl; } int main() { printColor(RedT() + GreenT()); printColor(RedT() + GreenT() + BlueT()); printColor(RedT() + (GreenT() + BlueT())); return 0; }

This example constructs a mini-library for linear algebra, using expression templates to eliminate the need for temporaries when adding arrays of numbers. It duplicates the TArray example from PETE.
這個例子構造了一個線性代數的小型庫,使用表達式模板來消除進行數值數組加法時所需的臨時對象。它複製了 PETE 中的TArray例子。

///////////////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This example constructs a mini-library for linear algebra, using
// expression templates to eliminate the need for temporaries when
// adding arrays of numbers. It duplicates the TArray example from
// PETE (http://www.codesourcery.com/pooma/download.html)
// 這個例子構造了一個線性代數的小型庫,使用表達式模板來消除進行數值數組加法時所需的臨時對象。
// 它複製了PETE(http://www.codesourcery.com/pooma/download.html)中的TArray例子。
#include <iostream> #include <boost/mpl/int.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // This grammar describes which TArray expressions
// are allowed; namely, int and array terminals
// plus, minus, multiplies and divides of TArray expressions.
// 這個語法描述了哪些TArray表達式是允許的;即,int和數組終結符,
// 以及TArray表達式的加、減、乘、除。
struct TArrayGrammar : proto::or_< proto::terminal< int > , proto::terminal< int[3] > , proto::plus< TArrayGrammar, TArrayGrammar > , proto::minus< TArrayGrammar, TArrayGrammar > , proto::multiplies< TArrayGrammar, TArrayGrammar > , proto::divides< TArrayGrammar, TArrayGrammar > > {}; template<typename Expr> struct TArrayExpr; // Tell proto that in the TArrayDomain, all
// expressions should be wrapped in TArrayExpr<> and
// must conform to the TArrayGrammar
// 告訴proto,在TArrayDomain中,所有表達式都應被包裝在TArrayExap<>中,
// 且必須符合TArrayGrammar。
struct TArrayDomain : proto::domain<proto::generator<TArrayExpr>, TArrayGrammar> {}; // Here is an evaluation context that indexes into a TArray
// expression, and combines the result.
// 以下是一個求值上下文,對一個TArray表達式進行索引操作並組合出結果。
struct TArraySubscriptCtx : proto::callable_context< TArraySubscriptCtx const > { typedef int result_type; TArraySubscriptCtx(std::ptrdiff_t i) : i_(i) {} // Index array terminals with our subscript. Everything
// else will be handled by the default evaluation context.
// 用我們的下標操作符對數組終結符取索引。其它所有操作以缺省的求值上下文來處理。
int operator ()(proto::tag::terminal, int const (&data)[3]) const { return data[this->i_]; } std::ptrdiff_t i_; }; // Here is an evaluation context that prints a TArray expression.
// 以下是一個求值上下文,打印一個TArray表達式。
struct TArrayPrintCtx : proto::callable_context< TArrayPrintCtx const > { typedef std::ostream &result_type; TArrayPrintCtx() {} std::ostream &operator ()(proto::tag::terminal, int i) const { return std::cout << i; } std::ostream &operator ()(proto::tag::terminal, int const (&arr)[3]) const { return std::cout << '{' << arr[0] << ", " << arr[1] << ", " << arr[2] << '}'; } template<typename L, typename R> std::ostream &operator ()(proto::tag::plus, L const &l, R const &r) const { return std::cout << '(' << l << " + " << r << ')'; } template<typename L, typename R> std::ostream &operator ()(proto::tag::minus, L const &l, R const &r) const { return std::cout << '(' << l << " - " << r << ')'; } template<typename L, typename R> std::ostream &operator ()(proto::tag::multiplies, L const &l, R const &r) const { return std::cout << l << " * " << r; } template<typename L, typename R> std::ostream &operator ()(proto::tag::divides, L const &l, R const &r) const { return std::cout << l << " / " << r; } }; // Here is the domain-specific expression wrapper, which overrides
// operator [] to evaluate the expression using the TArraySubscriptCtx.
// 以下是領域專用表達式的包裝器,它重載了operator[],以TArraySubscriptCtx來對表達式求值。
template<typename Expr> struct TArrayExpr : proto::extends<Expr, TArrayExpr<Expr>, TArrayDomain> { typedef proto::extends<Expr, TArrayExpr<Expr>, TArrayDomain> base_type; TArrayExpr( Expr const & expr = Expr() ) : base_type( expr ) {} // Use the TArraySubscriptCtx to implement subscripting
// of a TArray expression tree.
// 使用TArraySubscriptCtx來實現一棵TArray表達式樹的下標操作。
int operator []( std::ptrdiff_t i ) const { TArraySubscriptCtx const ctx(i); return proto::eval(*this, ctx); } // Use the TArrayPrintCtx to display a TArray expression tree.
// 使用TArrayPrintCtx來打印一棵TArray表達式樹。
friend std::ostream &operator <<(std::ostream &sout, TArrayExpr<Expr> const &expr) { TArrayPrintCtx const ctx; return proto::eval(expr, ctx); } }; // Here is our TArray terminal, implemented in terms of TArrayExpr
// It is basically just an array of 3 integers.
// 這是我們的TArray終結符,依照TArrayExpr來實現。它只是一個三個整數的數組。
struct TArray : TArrayExpr< proto::terminal< int[3] >::type > { explicit TArray( int i = 0, int j = 0, int k = 0 ) { (*this)[0] = i; (*this)[1] = j; (*this)[2] = k; } // Here we override operator [] to give read/write access to
// the elements of the array. (We could use the TArrayExpr
// operator [] if we made the subscript context smarter about
// returning non-const reference when appropriate.)
// 這裡我們重載了operator[],提供對數組元素的讀寫訪問。(如果我們讓下標操作上下文
// 更聰明些,在適當的時候返回非常量引用的話,就可以使用TArrayExpr的operator[])
int &operator [](std::ptrdiff_t i) { return proto::value(*this)[i]; } int const &operator [](std::ptrdiff_t i) const { return proto::value(*this)[i]; } // Here we define a operator = for TArray terminals that
// takes a TArray expression.
// 這裡我們為TArray終結符定義一個operator=,接受一個TArray表達式。
template< typename Expr > TArray &operator =(Expr const & expr) { // proto::as_expr<TArrayDomain>(expr) is the same as
// expr unless expr is an integer, in which case it
// is made into a TArrayExpr terminal first.
// proto::as_expr<TArrayDomain>(expr)和expr是一樣的,除非
// expr是一個整數,這種情況下它首先被製成一個TArrayExpr終結符。
return this->assign(proto::as_expr<TArrayDomain>(expr)); } template< typename Expr > TArray &printAssign(Expr const & expr) { *this = expr; std::cout << *this << " = " << expr << std::endl; return *this; } private: template< typename Expr > TArray &assign(Expr const & expr) { // expr[i] here uses TArraySubscriptCtx under the covers.
// 這裡的expr[i]在底層使用了TArraySubscriptCtx。
(*this)[0] = expr[0]; (*this)[1] = expr[1]; (*this)[2] = expr[2]; return *this; } }; int main() { TArray a(3,1,2); TArray b; std::cout << a << std::endl; std::cout << b << std::endl; b[0] = 7; b[1] = 33; b[2] = -99; TArray c(a); std::cout << c << std::endl; a = 0; std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl; a = b + c; std::cout << a << std::endl; a.printAssign(b+c*(b + 3*c)); return 0; }

This is a simple example using proto::extends<> to extend a terminal type with additional behaviors, and using custom contexts and proto::eval() for evaluating expressions. It is a port of the Vec3 example from PETE.
這是一個簡單的例子,使用 proto::extends<> 來擴展一個終結符類型以增加行為,並使用定制的上下文和 proto::eval() 來對表達式求值。它是修改自 PETE 中的Vec3例子。

///////////////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This is a simple example using proto::extends to extend a terminal type with
// additional behaviors, and using custom contexts and proto::eval for
// evaluating expressions. It is a port of the Vec3 example
// from PETE (http://www.codesourcery.com/pooma/download.html).
// 這是一個簡單的例子,使用proto::extends來擴展一個終結符類型,增加其行為,並使用定制的上下文和proto::eval
// 來對表達式求值。它是修改自PETE(http://www.codesourcery.com/pooma/download.html)的Vec3例子。
#include <iostream> #include <functional> #include <boost/assert.hpp> #include <boost/mpl/int.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> #include <boost/proto/proto_typeof.hpp> #include <boost/proto/transform.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // Here is an evaluation context that indexes into a Vec3
// expression, and combines the result.
// 以下是一個求值上下文,對Vec3表達式進行索引操作,並組合成結果。
struct Vec3SubscriptCtx : proto::callable_context< Vec3SubscriptCtx const > { typedef int result_type; Vec3SubscriptCtx(int i) : i_(i) {} // Index array terminals with our subscript. Everything
// else will be handled by the default evaluation context.
// 用我們的下標操作對數組終結符求索引。其它所有操作用缺省求值上下文來處理。
int operator ()(proto::tag::terminal, int const (&arr)[3]) const { return arr[this->i_]; } int i_; }; // Here is an evaluation context that counts the number
// of Vec3 terminals in an expression.
// 以下是一個求值上下文,計算一個表達式中的Vec3終結符數量。
struct CountLeavesCtx : proto::callable_context< CountLeavesCtx, proto::null_context > { CountLeavesCtx() : count(0) {} typedef void result_type; void operator ()(proto::tag::terminal, int const(&)[3]) { ++this->count; } int count; }; struct iplus : std::plus<int>, proto::callable {}; // Here is a transform that does the same thing as the above context.
// It demonstrates the use of the std::plus<> function object
// with the fold transform. With minor modifications, this
// transform could be used to calculate the leaf count at compile
// time, rather than at runtime.
// 以下是一個變換,完成與前述上下文相同的工作。它示範了把std::plus<>函數對像用於fold變換。
// 通過少許修改,這個變換可以用於在編譯期計算葉子數量,而不是在運行期。
struct CountLeaves : proto::or_< // match a Vec3 terminal, return 1 匹配一個Vec3終結符,返回1
proto::when<proto::terminal<int[3]>, mpl::int_<1>() > // match a terminal, return int() (which is 0) 匹配一個終結符,返回int()(為0)
, proto::when<proto::terminal<_>, int() > // fold everything else, using std::plus<> to add
// the leaf count of each child to the accumulated state.
// 折疊其它所有東西,使用std::plus<>將所有子節點的葉子數量累加到累計狀態中。
, proto::otherwise< proto::fold<_, int(), iplus(CountLeaves, proto::_state) > > > {}; // Here is the Vec3 struct, which is a vector of 3 integers.
// 以下是Vec3的結構,它是一個帶3個整數的向量。
struct Vec3 : proto::extends<proto::terminal<int[3]>::type, Vec3> { explicit Vec3(int i=0, int j=0, int k=0) { (*this)[0] = i; (*this)[1] = j; (*this)[2] = k; } int &operator [](int i) { return proto::value(*this)[i]; } int const &operator [](int i) const { return proto::value(*this)[i]; } // Here we define a operator = for Vec3 terminals that
// takes a Vec3 expression.
// 這裡我們為Vec3終結符定義一個operator=,接受一個Vec3表達式。
template< typename Expr > Vec3 &operator =(Expr const & expr) { typedef Vec3SubscriptCtx const CVec3SubscriptCtx; (*this)[0] = proto::eval(proto::as_expr(expr), CVec3SubscriptCtx(0)); (*this)[1] = proto::eval(proto::as_expr(expr), CVec3SubscriptCtx(1)); (*this)[2] = proto::eval(proto::as_expr(expr), CVec3SubscriptCtx(2)); return *this; } void print() const { std::cout << '{' << (*this)[0] << ", " << (*this)[1] << ", " << (*this)[2] << '}' << std::endl; } }; // The count_leaves() function uses the CountLeaves transform and
// to count the number of leaves in an expression.
// 函數count_leaves()使用CountLeaves變換來計算一個表達式中的葉子數量。
template<typename Expr> int count_leaves(Expr const &expr) { // Count the number of Vec3 terminals using the
// CountLeavesCtx evaluation context.
// 使用CountLeavesCtx求值上下文計算Vec3終結符的數量。
CountLeavesCtx ctx; proto::eval(expr, ctx); // This is another way to count the leaves using a transform.
// 這是使用變換來計算葉子數量的另一個方法。
int i = 0; BOOST_ASSERT( CountLeaves()(expr, i, i) == ctx.count ); return ctx.count; } int main() { Vec3 a, b, c; c = 4; b[0] = -1; b[1] = -2; b[2] = -3; a = b + c; a.print(); Vec3 d; BOOST_PROTO_AUTO(expr1, b + c); d = expr1; d.print(); int num = count_leaves(expr1); std::cout << num << std::endl; BOOST_PROTO_AUTO(expr2, b + 3 * c); num = count_leaves(expr2); std::cout << num << std::endl; BOOST_PROTO_AUTO(expr3, b + c * d); num = count_leaves(expr3); std::cout << num << std::endl; return 0; }

This is an example of using BOOST_PROTO_DEFINE_OPERATORS() to Protofy expressions using std::vector<>, a non-Proto type. It is a port of the Vector example from PETE.
這個例子使用 BOOST_PROTO_DEFINE_OPERATORS() 來Proto化使用了一個非Proto類型 std::vector<> 的表達式。它修改自 PETE 的Vector例子。

///////////////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This is an example of using BOOST_PROTO_DEFINE_OPERATORS to Protofy
// expressions using std::vector<>, a non-proto type. It is a port of the
// Vector example from PETE (http://www.codesourcery.com/pooma/download.html).
// 這個例子使用BOOST_PROTO_DEFINE_OPERATORS()來Proto化使用了一個非Proto類型std::vector<>
// 的表達式。它修改自PETE(http://www.codesourcery.com/pooma/download.html)的Vector例子。
#include <vector> #include <iostream> #include <stdexcept> #include <boost/mpl/bool.hpp> #include <boost/proto/core.hpp> #include <boost/proto/debug.hpp> #include <boost/proto/context.hpp> #include <boost/utility/enable_if.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; template<typename Expr> struct VectorExpr; // Here is an evaluation context that indexes into a std::vector
// expression and combines the result.
// 以下是一個求值上下文,對一個std::vector表達式求索引並組合出結果。
struct VectorSubscriptCtx { VectorSubscriptCtx(std::size_t i) : i_(i) {} // Unless this is a vector terminal, use the
// default evaluation context
// 除非是vector終結符,否則使用缺省求值上下文
template<typename Expr, typename EnableIf = void> struct eval : proto::default_eval<Expr, VectorSubscriptCtx const> {}; // Index vector terminals with our subscript.
// 用我們的下標操作對vector終結符求索引。
template<typename Expr> struct eval< Expr , typename boost::enable_if< proto::matches<Expr, proto::terminal<std::vector<_, _> > > >::type > { typedef typename proto::result_of::value<Expr>::type::value_type result_type; result_type operator ()(Expr &expr, VectorSubscriptCtx const &ctx) const { return proto::value(expr)[ctx.i_]; } }; std::size_t i_; }; // Here is an evaluation context that verifies that all the
// vectors in an expression have the same size.
// 以下是一個求值上下文,驗證表達式中的所有向量具有相同的大小。
struct VectorSizeCtx { VectorSizeCtx(std::size_t size) : size_(size) {} // Unless this is a vector terminal, use the
// null evaluation context
// 除非是vector終結符,否則使用空求值上下文
template<typename Expr, typename EnableIf = void> struct eval : proto::null_eval<Expr, VectorSizeCtx const> {}; // Index array terminals with our subscript. Everything
// else will be handled by the default evaluation context.
// 用我們的下標操作對數組終結符求索引。其它所有東西由缺省求值上下文處理。
template<typename Expr> struct eval< Expr , typename boost::enable_if< proto::matches<Expr, proto::terminal<std::vector<_, _> > > >::type > { typedef void result_type; result_type operator ()(Expr &expr, VectorSizeCtx const &ctx) const { if(ctx.size_ != proto::value(expr).size()) { throw std::runtime_error("LHS and RHS are not compatible"); } } }; std::size_t size_; }; // A grammar which matches all the assignment operators,
// so we can easily disable them.
// 匹配所有賦值操作符的語法,我們可以很容易地禁用它們。
struct AssignOps : proto::switch_<struct AssignOpsCases> {}; // Here are the cases used by the switch_ above.
// 以下是上述switch_要使用的情形。
struct AssignOpsCases { template<typename Tag, int D = 0> struct case_ : proto::not_<_> {}; template<int D> struct case_< proto::tag::plus_assign, D > : _ {}; template<int D> struct case_< proto::tag::minus_assign, D > : _ {}; template<int D> struct case_< proto::tag::multiplies_assign, D > : _ {}; template<int D> struct case_< proto::tag::divides_assign, D > : _ {}; template<int D> struct case_< proto::tag::modulus_assign, D > : _ {}; template<int D> struct case_< proto::tag::shift_left_assign, D > : _ {}; template<int D> struct case_< proto::tag::shift_right_assign, D > : _ {}; template<int D> struct case_< proto::tag::bitwise_and_assign, D > : _ {}; template<int D> struct case_< proto::tag::bitwise_or_assign, D > : _ {}; template<int D> struct case_< proto::tag::bitwise_xor_assign, D > : _ {}; }; // A vector grammar is a terminal or some op that is not an
// assignment op. (Assignment will be handled specially.)
// 一個vector語法是一個終結符,或某個不是賦值操作的操作。(賦值操作要特殊處理)
struct VectorGrammar : proto::or_< proto::terminal<_> , proto::and_<proto::nary_expr<_, proto::vararg<VectorGrammar> >, proto::not_<AssignOps> > > {}; // Expressions in the vector domain will be wrapped in VectorExpr<>
// and must conform to the VectorGrammar
// vector領域中的表達式要包裝在VectorExpr<>中,並且必須符合VectorGrammar
struct VectorDomain : proto::domain<proto::generator<VectorExpr>, VectorGrammar> {}; // Here is VectorExpr, which extends a proto expr type by
// giving it an operator [] which uses the VectorSubscriptCtx
// to evaluate an expression with a given index.
// 以下是VectorExpr,它擴展了一個proto expr類型,增加了一個operator[],
// 使用VectorSubscriptCtx以給定的索引值對一個表達式求值。
template<typename Expr> struct VectorExpr : proto::extends<Expr, VectorExpr<Expr>, VectorDomain> { explicit VectorExpr(Expr const &expr) : proto::extends<Expr, VectorExpr<Expr>, VectorDomain>(expr) {} // Use the VectorSubscriptCtx to implement subscripting
// of a Vector expression tree.
// 使用VectorSubscriptCtx來實現Vector表達式樹的下標操作。
typename proto::result_of::eval<Expr const, VectorSubscriptCtx const>::type operator []( std::size_t i ) const { VectorSubscriptCtx const ctx(i); return proto::eval(*this, ctx); } }; // Define a trait type for detecting vector terminals, to
// be used by the BOOST_PROTO_DEFINE_OPERATORS macro below.
// 定義一個trait類型,檢測vector終結符,用於後面的BOOST_PROTO_DEFINE_OPERATORS宏。
template<typename T> struct IsVector : mpl::false_ {}; template<typename T, typename A> struct IsVector<std::vector<T, A> > : mpl::true_ {}; namespace VectorOps { // This defines all the overloads to make expressions involving
// std::vector to build expression templates.
// 這裡定義從一個含有std::vector的表達式構建出表達式模板所需的所有重載。
BOOST_PROTO_DEFINE_OPERATORS(IsVector, VectorDomain) typedef VectorSubscriptCtx const CVectorSubscriptCtx; // Assign to a vector from some expression.
// 從某個表達式賦值到一個vector中。
template<typename T, typename A, typename Expr> std::vector<T, A> &assign(std::vector<T, A> &arr, Expr const &expr) { VectorSizeCtx const size(arr.size()); proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match 如果大小不匹配則拋出
for(std::size_t i = 0; i < arr.size(); ++i) { arr[i] = proto::as_expr<VectorDomain>(expr)[i]; } return arr; } // Add-assign to a vector from some expression.
// 從某個表達式加賦值至一個vector。
template<typename T, typename A, typename Expr> std::vector<T, A> &operator +=(std::vector<T, A> &arr, Expr const &expr) { VectorSizeCtx const size(arr.size()); proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match 如果大小不匹配則拋出
for(std::size_t i = 0; i < arr.size(); ++i) { arr[i] += proto::as_expr<VectorDomain>(expr)[i]; } return arr; } } int main() { using namespace VectorOps; int i; const int n = 10; std::vector<int> a,b,c,d; std::vector<double> e(n); for (i = 0; i < n; ++i) { a.push_back(i); b.push_back(2*i); c.push_back(3*i); d.push_back(i); } VectorOps::assign(b, 2); VectorOps::assign(d, a + b * c); a += if_else(d < 30, b, c); VectorOps::assign(e, c); e += e - 4 / (c + 1); for (i = 0; i < n; ++i) { std::cout << " a(" << i << ") = " << a[i] << " b(" << i << ") = " << b[i] << " c(" << i << ") = " << c[i] << " d(" << i << ") = " << d[i] << " e(" << i << ") = " << e[i] << std::endl; } }

This is an example of using BOOST_PROTO_DEFINE_OPERATORS() to Protofy expressions using std::vector<> and std::list<>, non-Proto types. It is a port of the Mixed example from PETE.
這是一個使用 BOOST_PROTO_DEFINE_OPERATORS() 來對使用了 std::vector<>std::list<> 這些非Proto類型的表達式進行Proto化的例子。它修改自 PETE 的Mixed例子。

///////////////////////////////////////////////////////////////////////////////
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This is an example of using BOOST_PROTO_DEFINE_OPERATORS to Protofy
// expressions using std::vector<> and std::list, non-proto types. It is a port
// of the Mixed example from PETE.
// 這是一個使用BOOST_PROTO_DEFINE_OPERATORS來對使用了std::vector<>和std::list這些
//
非Proto類型的表達式進行Proto化的例子。它修改自PETE的Mixed例子。
// (http://www.codesourcery.com/pooma/download.html).

#include <list> #include <cmath> #include <vector> #include <complex> #include <iostream> #include <stdexcept> #include <boost/proto/core.hpp> #include <boost/proto/debug.hpp> #include <boost/proto/context.hpp> #include <boost/proto/transform.hpp> #include <boost/utility/enable_if.hpp> #include <boost/typeof/std/list.hpp> #include <boost/typeof/std/vector.hpp> #include <boost/typeof/std/complex.hpp> #include <boost/type_traits/remove_reference.hpp> namespace proto = boost::proto; namespace mpl = boost::mpl; using proto::_; template<typename Expr> struct MixedExpr; template<typename Iter> struct iterator_wrapper { typedef Iter iterator; explicit iterator_wrapper(Iter iter) : it(iter) {} Iter it; }; struct begin : proto::callable { template<class Sig> struct result; template<class This, class Cont> struct result<This(Cont)> : proto::result_of::as_expr< iterator_wrapper<typename boost::remove_reference<Cont>::type::const_iterator> > {}; template<typename Cont> typename result<begin(Cont const &)>::type operator ()(Cont const &cont) const { iterator_wrapper<typename Cont::const_iterator> it(cont.begin()); return proto::as_expr(it); } }; // Here is a grammar that replaces vector and list terminals with their
// begin iterators
// 這是將vector和list替換為它們的begin迭代器的語法
struct Begin : proto::or_< proto::when< proto::terminal< std::vector<_, _> >, begin(proto::_value) > , proto::when< proto::terminal< std::list<_, _> >, begin(proto::_value) > , proto::when< proto::terminal<_> > , proto::when< proto::nary_expr<_, proto::vararg<Begin> > > > {}; // Here is an evaluation context that dereferences iterator
// terminals.
// 這是對迭代器終結符解引用的求值上下文。
struct DereferenceCtx { // Unless this is an iterator terminal, use the
// default evaluation context
// 除非是一個迭代器終結符,否則使用缺省的求值上下文
template<typename Expr, typename EnableIf = void> struct eval : proto::default_eval<Expr, DereferenceCtx const> {}; // Dereference iterator terminals. 對迭代器終結符解引用。
template<typename Expr> struct eval< Expr , typename boost::enable_if< proto::matches<Expr, proto::terminal<iterator_wrapper<_> > > >::type > { typedef typename proto::result_of::value<Expr>::type IteratorWrapper; typedef typename IteratorWrapper::