Boost Parameter 庫

Boost


Abstract 摘要:

Use this library to write functions and class templates that can accept arguments by name:

使用這個庫,可以寫出通過名字傳參數的函數或者類模板:

new_window("alert", _width=10, _titlebar=false);

smart_ptr<
Foo
, deleter<Deallocate<Foo> >
, copy_policy<DeepCopy> > p(new Foo);

Since named arguments can be passed in any order, they are especially useful when a function or template has more than one parameter with a useful default value. The library also supports deduced parameters; that is to say, parameters whose identity can be deduced from their types.

傳遞有名參數可以不考慮參數之間的順序,這樣在寫帶有參數默認值的函數或者模板 時,這種傳參方式會來帶很多便利。這個庫也支持 推導的(deduced) 參數;也就是說,根據參數的類型可以 推導出參數標識符(identity)的參數。


作 者: David Abrahams, Daniel Wallin
聯繫 方式: dave@boost-consulting.com, dalwan01@student.umu.se
組 織: Boost Consulting
日 期: $Date: 2005/07/18 20:34:31 $
版 權: Copyright David Abrahams, Daniel Wallin 2005. 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)

[Note: this tutorial does not cover all details of the library. Please see also the reference documentation]
[註:本指南並未覆蓋本庫所有細節。請參見 參考文檔]

Table of Contents 目錄


1   Motivation 動機

In C++, arguments are normally given meaning by their positions with respect to a parameter list: the first argument passed maps onto the first parameter in a function's definition, and so on. That protocol is fine when there is at most one parameter with a default value, but when there are even a few useful defaults, the positional interface becomes burdensome:

在C++中,參數(arguments) 的含義通常是由它們在參變量(parameter) 列表中的位置決定的:第一個參數對應了函數定義中的第一個參變量,如此等等。這個約定(protocol)在最多只有一個參變量帶有默認值的時候還是比較 好用的,但是當有許多有用的默認值時,這種位置相關的接口方式就變得笨拙起來。

1.1   Named Function Parameters 有名的函數參變量

This library addresses the problems outlined above by associating each parameter name with a keyword object. Now users can identify arguments by name, rather than by position:

這個庫通過將每個參變量名和一個關鍵字對像聯繫起來,解決了上面提出的 問題。現在用戶可以通過名字來識別參數,而不是位置:

window* w = new_window("alert box", movable_=false); // OK!

1.2   Deduced Function Parameters 推導的函數參變量

A deduced parameter can be passed in any position without supplying an explicit parameter name. It's not uncommon for a function to have parameters that can be uniquely identified based on the types of arguments passed. The name parameter to new_window is one such example. None of the other arguments, if valid, can reasonably be converted to a char const*. With a deduced parameter interface, we could pass the window name in any argument position without causing ambiguity:

推導的參變量(deduced parameter) 可以在任何位置傳 入,而 不需要 提供顯式的參變量名字。對於一個函數來說,可以通過傳入參數的類型來唯一地識別出函數的參變量, 這樣的情況並不少見。對於 new_window 來說, name 參變量就是個例子。其他的參數,如果正確的話,都不能合理地轉化成 char const*
類型。有了一個可推導的參變量接口,我們就可以在 任意 一個參數位置傳入 window 的 name,而不會產生歧義。

window* w = new_window(movable_=false, "alert box"); // OK!
window* w = new_window("alert box", movable_=false); // OK!

Appropriately used, a deduced parameter interface can free the user of the burden of even remembering the formal parameter names.

適當地使用,可推導的參變量接口可以減輕記憶正規參變量名的負擔。

1.3   Class Template Parameter Support 類模板參變量的支持

The reasoning we've given for named and deduced parameter interfaces applies equally well to class templates as it does to functions. Using the Parameter library, we can create interfaces that allow template arguments (in this case shared and Client) to be explicitly named, like this:

對於函數,我們需要可推導的參變量接口,同樣的理由也適用於類模板。使用 Parameter庫,我們可以創建顯示命名的模板參數(在這裡是 shared
Client
),就像這樣:

smart_ptr<ownership<shared>, value_type<Client> > p;

The syntax for passing named template arguments is not quite as natural as it is for function arguments (ideally, we'd be able to write smart_ptr<ownership=shared,…>). This small syntactic deficiency makes deduced parameters an especially big win when used with class templates:

傳遞有名模板參數的語法不像函數參數那樣自然(理想情況下,我們希望 能這樣寫 smart_ptr<ownership=shared,…>)。 這個小的語法瑕疵使得可推導的參變量在使用到類模板中時贏得了巨大的成功。

// p and q could be equivalent, given a deduced
// parameter interface. 在可推導參變量接口的情況下,p和q是等價的
smart_ptr<shared, Client> p;
smart_ptr<Client, shared> q;

2   Tutorial 教程

This tutorial shows all the basics—how to build both named- and deduced-parameter interfaces to function templates and class templates—and several more advanced idioms as well.

這個教程展示了所有的基本知識——如何為函數和類模板定義有名的以及可推導的參變量接口——和一些高級的慣用手法。

2.1   Parameter-Enabled Functions Boost.Parameter式的函數

In this section we'll show how the Parameter library can be used to build an expressive interface to the Boost Graph library's depth_first_search algorithm.1

在這一節中,我們將展示Parameter庫是如何定義一個清晰易懂的(expressive)接口,被Boost Graph librarydepth_first_search 算法所使用的。

2.1.1   Headers And Namespaces 頭文件和名字空間(namespace)

Most components of the Parameter library are declared in a header named for the component. For example,

這個庫(Parameter)大多數的組件都被聲明在一個以這個庫命名的頭文件中。比如,

#include <boost/parameter/keyword.hpp>

will ensure boost::parameter::keyword is known to the compiler. There is also a combined header, boost/parameter.hpp, that includes most of the library's components. For the the rest of this tutorial, unless we say otherwise, you can use the rule above to figure out which header to #include to access any given component of the library.

將確保編譯器知道 boost::parameter::keyword。 也有一個組合的頭文件
boost/parameter.hpp
,它包括了這個庫的大部分組件。對於這個教程的剩下部分,你在 需要訪問這個庫的某個組件時,可以使用上面的這條規則來找到所需要 #include 的頭文件,除非我們特別說明。

Also, the examples below will also be written as if the namespace alias

而且,接下來的例子可能會使用一個名字空間的別名,就像

namespace parameter = boost::parameter;

has been declared: we'll write parameter::xxx instead of boost::parameter::xxx.

聲明的那樣:我們會寫 parameter::xxx 來代替 boost::parameter::xxx

2.1.2   The Abstract Interface to depth_first_search depth_first_search的 抽像接口

The Graph library's depth_first_search algorithm is a generic function accepting from one to four arguments by reference. If all arguments were required, its signature might be as follows:

Graph庫的 depth_first_search 算法是一個泛型函數,它接受一到四個引用參數。如果所有的參數都是必須的,它的函數簽名(signature)就會是這樣的:

template <
class Graph, class DFSVisitor, class Index, class ColorMap
>
void depth_first_search(
, Graph const& graph
, DFSVisitor visitor
, typename graph_traits<g>::vertex_descriptor root_vertex
, IndexMap index_map
, ColorMap& color);

However, most of the parameters have a useful default value, as shown in the table below.

然而,大多數的參變量都有一個有用的默認值,如下表所示。

depth_first_search Parameters
參變量(Parameter)名 數據流向 變量類型(Type) 默認值(如果有的話)
graph in 傳入 Model of Incidence Graph and Vertex List Graph
Incidence GraphVertex List Graph 類型的模式(model)
none - this argument is required.
無——這個參數是必須的。
visitor in 傳入 Model of DFS Visitor boost::dfs_visitor<>()
root_vertex in 傳入 graph's vertex descriptor type.
graph 的頂點描述符(vertex descriptor)類型。
*vertices(graph).first
index_map in 傳入 Model of Readable Property Map with key type := graph's vertex descriptor and value type an integer type.
Readable Property Map 模式(model),其 key 類型 := graph 的頂點描述符(vertex descriptor)類型,且 value 類型是整型。
get(boost::vertex_index,graph)
color_map in/out 傳入/傳出 Model of Read/Write Property Map with key type := graph's vertex descriptor type.
Read/Write Property Map 模式(model),其 key 類型 := graph 的頂點描述符(vertex descriptor)類型.
an iterator_property_map created from a std::vector of default_color_type of size num_vertices(graph) and using index_map for the index map.
從一個大小為 num_vertices(graph) , 類型為 default_color_type std::vector 創建的 iterator_property_map, 並且使用了 index_map 來做索引映射。

Don't be intimidated by the information in the second and third columns above. For the purposes of this exercise, you don't need to understand them in detail.

不要被上面第二和第三列的內容嚇到了。對這個練習來說,你不需要理解他們的細節。

2.1.3   Defining the Keywords 定義關鍵字(Keywords)

The point of this exercise is to make it possible to call depth_first_search with named arguments, leaving out any arguments for which the default is appropriate:

這個練習的關鍵是要使我們可以通過有名參數來調用 depth_first_search 函數,並且讓其他所有的參數都使用自己的默認值。

graphs::depth_first_search(g, color_map_=my_color_map);

To make that syntax legal, there needs to be an object called 「color_map_」 whose assignment operator can accept a my_color_map argument. In this step we'll create one such keyword object for each parameter. Each keyword object will be identified by a unique keyword tag type.

為了是上面的語法能夠使用(legal),我們需要定義一個叫做「color_map_」並且它的 賦值操作符(=)能夠接受參數 my_color_map 的對象。在這一步裡 面,我們將為每一個參變量創建一個這樣的關鍵字對像(keyword object)。每一個關鍵字都可以通過一個唯一 的 關鍵字tag類型(keyword tag type) 進行標識。

We're going to define our interface in namespace graphs. The library provides a convenient macro for defining keyword objects:

我們在名字空間 graphs 中定義自己的接口。這個庫提供了一個方便的宏來定義關鍵字對像:

#include <boost/parameter/name.hpp>

namespace graphs
{
BOOST_PARAMETER_NAME(graph) // Note: no semicolon 注意:沒有分號
BOOST_PARAMETER_NAME(visitor)
BOOST_PARAMETER_NAME(root_vertex)
BOOST_PARAMETER_NAME(index_map)
BOOST_PARAMETER_NAME(color_map)
}

The declaration of the graph keyword you see here is equivalent to:

你看到這裡聲明的 graph 關鍵字等價於:

namespace graphs
{
namespace tag { struct graph; } // 關鍵字標籤類型(keyword tag type)

namespace // 無名的名字空間
{
// A reference to the keyword object 對關鍵字對象的引用
boost::parameter::keyword<tag::graph>& _graph
= boost::parameter::keyword<tag::graph>::get();
}
}

It defines a keyword tag type named tag::graph and a keyword object reference named _graph.

它定義了一個叫做 tag::graph關 鍵字tag類型(keyword tag type) 和 一個引用名為 _graph關鍵字對像(keyword object)。

This 「fancy dance」 involving an unnamed namespace and references is all done to avoid violating the One Definition Rule (ODR)2 when the named parameter interface is used by function templates that are instantiated in multiple translation units (MSVC6.x users see this note).

這裡包括無名名字空間和引用在內的有趣東西( 「fancy dance」)是為了:在多個翻譯單元(translation unit)(MSVC6.x 用戶請查看 這 個注意事項)中被實例化函數模板使用有名參數時,避免與One Definition Rule (ODR)2規則相衝突。

2.1.4   Writing the Function 寫函數

Now that we have our keywords defined, the function template definition follows a simple pattern using the BOOST_PARAMETER_FUNCTION macro:

現在我們已經有了定義好的關鍵字,函數模板的定義可以使用 BOOST_PARAMETER_FUNCTION 宏以一種簡單的模式來定義:

#include <boost/parameter/preprocessor.hpp>

namespace graphs
{
BOOST_PARAMETER_FUNCTION(
(void), // 1. parenthesized return type 圓括號括起來的返回類型
depth_first_search, // 2. name of the function template 函數模板的名字

tag, // 3. namespace of tag types 標籤類型(tag type)的名字空間

(required (graph, *) ) // 4. one required parameter, and 一個必選的參變量,和

(optional // four optional parameters, with defaults 四個可選的參變量,以及它們的默認值
(visitor, *, boost::dfs_visitor<>())
(root_vertex, *, *vertices(graph).first)
(index_map, *, get(boost::vertex_index,graph))
(in_out(color_map), *,
default_color_map(num_vertices(graph), index_map) )
)
)
{
// ... body of function goes here... 函數體
// use graph, visitor, index_map, and color_map
}
}

The arguments to BOOST_PARAMETER_FUNCTION are:  BOOST_PARAMETER_FUNCTION 宏 的參數如下:

  1. The return type of the resulting function template. Parentheses around the return type prevent any commas it might contain from confusing the preprocessor, and are always required. 函數模板的返回類型。返回類型用圓括號括起來可以防止它可能包含的逗號干擾處理器,並且括號是必須的。
  2. The name of the resulting function template.函數模板的名字。
  3. The name of a namespace where we can find tag types whose names match the function's parameter names. 名字空間,我們可以從這個名字空間中找到名字與函數參變量名相匹配的tag類型。
  4. The function signature. 函數簽名(signature)。

2.1.5   Function Signatures 函數簽名(Signature)

Function signatures are described as one or two adjacent parenthesized terms (a Boost.Preprocessor sequence) describing the function's parameters in the order in which they'd be expected if passed positionally. Any required parameters must come first, but the (required ) clause can be omitted when all the parameters are optional.

函數簽名是由一個或兩個相鄰的帶圓括號的短語(一個Boost.Preprocessor sequence) 來描述的。

2.1.5.1   Required Parameters 必選的參變量

Required parameters are given first—nested in a (required ) clause—as a series of two-element tuples describing each parameter name and any requirements on the argument type. In this case there is only a single required parameter, so there's just a single tuple:

必選的參變量——嵌在一個(required )語句中——以一系列連續的二元組形 式給出,每個二元組描述一個參變量的名字和對參數類型的限制條件。在這個練習中,只有一個必選的參變量,所以這裡只有 一個元組:

(required (graph, *) )

Since depth_first_search doesn't require any particular type for its graph parameter, we use an asterix to indicate that any type is allowed. Required parameters must always precede any optional parameters in a signature, but if there are no required parameters, the (required ) clause can be omitted entirely.

由於 depth_first_searchgraph 參變量不針對任何特定的類型,我們使用一個星號來表示任何類型都是允許的。在函數簽名(signature)中,必選的參變量必須在任何可選參變量的前 面, 但是如果沒有必選的參變量,(required ) 語句可以被完全省略。

2.1.5.2   Optional Parameters 可選的參變量

Optional parameters—nested in an (optional ) clause—are given as a series of adjacent three-element tuples describing the parameter name, any requirements on the argument type, and and an expression representing the parameter's default value:

可選參變量——嵌在一個(optional )語句中— —以一系列連續的三元組形式給出,每個三元組描述一個參變量的名字,對參數類型的限制條件以及表示這個參數默認值的表達式:

(optional
(visitor, *, boost::dfs_visitor<>())
(root_vertex, *, *vertices(graph).first)
(index_map, *, get(boost::vertex_index,graph))
(in_out(color_map), *,
default_color_map(num_vertices(graph), index_map) )

)

2.1.5.3   Handling 「Out」 Parameters 處理Out參變量

Within the function body, a parameter name such as visitor is a C++ reference, bound either to an actual argument passed by the caller or to the result of evaluating a default expression. In most cases, parameter types are of the form T const& for some T. Parameters whose values are expected to be modified, however, must be passed by reference to non-const. To indicate that color_map is both read and written, we wrap its name in in_out(…):

在函數體中,參變量名(比如 visitor) 是一個 C++ 引用,綁定到調用者傳入的真實參數或者默認表達式的計算結果。在大多數情況下,對於某個類 型 T, 參變量都是 T const& 的形式。那些值會被修改的參變量必須以 non-const 引用的形式傳入。為了表示 color_map 是可讀寫的,我們將它的名字 包括在 in_out(…) 中:

(optional
(visitor, *, boost::dfs_visitor<>())
(root_vertex, *, *vertices(graph).first)
(index_map, *, get(boost::vertex_index,graph))
(in_out(color_map), *,
default_color_map(num_vertices(graph), index_map) )
)

If color_map were strictly going to be modified but not examined, we could have written out(color_map). There is no functional difference between out and in_out; the library provides both so you can make your interfaces more self-documenting.

如果 color_map 是一個只用於修改的值,而不需要進行參數檢查,我們可以這樣寫 out(color_map)。 在功能上,outin_out 沒有區別;這個庫提供這兩者是為了使你的接口更加一目瞭然(self-documenting)。

2.1.5.4   Positional Arguments 位置相關的參數

When arguments are passed positionally (without the use of keywords), they will be mapped onto parameters in the order the parameters are given in the signature, so for example in this call

當參數通過位置相關的(positional)方式傳入時(不使用關鍵字),他們將以函數簽名中給定的參變量順序映射到相應的參變量, 所以在下面這個調用的例子中

graphs::depth_first_search(x, y);

x will always be interpreted as a graph and y will always be interpreted as a visitor.

x 就會被解釋成一個 graph,y 就 會被解釋成一個 visitor。

2.1.5.5   Default Expression Evaluation 默認表達式的求值

Note that in our example, the value of the graph parameter is used in the default expressions for root_vertex, index_map and color_map.

請注意,在我們的例子中,graph 參變量的值被用在了 root_vertexindex_mapcolor_map
的默認表達式中。

(required (graph, *) )
(optional
(visitor, *, boost::dfs_visitor<>())
(root_vertex, *, *vertices(graph).first)
(index_map, *, get(boost::vertex_index,graph))
(in_out(color_map), *,
default_color_map(num_vertices(graph), index_map) )
)

A default expression is evaluated in the context of all preceding parameters, so you can use any of their values by name.

默認表達式的值是以它之前的所有參變量為上下文的環境中被計算的,因此你 可以通過名字來使用它們的值。

A default expression is never evaluated—or even instantiated—if an actual argument is passed for that parameter. We can actually demonstrate that with our code so far by replacing the body of depth_first_search with something that prints the arguments:

如果真實的參數被傳給了當前的參變量,默認表達式的值是不會被計 算的——甚至不會被實例化(譯註:編譯器不會編譯這一段)。在我們的代碼裡,我們現在可以通過在 depth_first_search 函數體中替換上一些打印參數的語句來演示這一點:

#include <boost/graph/depth_first_search.hpp> // for dfs_visitor

BOOST_PARAMETER_FUNCTION(
(void), depth_first_search, tag
…signature goes here…
)
{
std::cout << "graph=" << graph << std::endl;
std::cout << "visitor=" << visitor << std::endl;
std::cout << "root_vertex=" << root_vertex << std::endl;
std::cout << "index_map=" << index_map << std::endl;
std::cout << "color_map=" << color_map << std::endl;
}
int main()
{
depth_first_search(1, 2, 3, 4, 5);
depth_first_search(
"1", '2', _color_map = '5',
_index_map = "4", _root_vertex = "3");
}

Despite the fact that default expressions such as vertices(graph).first are ill-formed for the given graph arguments, both calls will compile, and each one will print exactly the same thing.

類似 vertices(graph).first 的默認表達式對例子中給定的 graph 參數來說是完全非法的,調用或者編譯都是,但是上面的例子卻會完整地打印出傳入的參數。

2.1.5.6   Signature Matching and Overloading 函數簽名(signature)匹配和函數重載(overloading)

In fact, the function signature is so general that any call to depth_first_search with fewer than five arguments will match our function, provided we pass something for the required graph parameter. That might not seem to be a problem at first; after all, if the arguments don't match the requirements imposed by the implementation of depth_first_search, a compilation error will occur later, when its body is instantiated.

實際上,這個函數的簽名是非常通用的,對depth_first_search 來說,只要我們為 graph 參變量傳入 某個值,任何少於五個參數的調用 都會匹配到這個函數。剛開始,這看起來可能沒有什麼問題;畢竟,如果參數跟 depth_first_search 的實現(所用到的參數類型)不匹配,在函數體被編譯時就會出現編譯錯誤。

There are at least three problems with very general function signatures.

對於這種非常通用的函數簽名,至少存在三個問題:

  1. By the time our depth_first_search is instantiated, it has been selected as the best matching overload. Some other depth_first_search overload might've worked had it been chosen instead. By the time we see a compilation error, there's no chance to change that decision.

    在我們的被 depth_first_search 實例化的時候,它已經是做為最合適的函數重載被選擇的。選擇其他的 depth_first_search 重載可能不能工作。當我們看到編譯錯誤的時候,已經沒有機會來改變這個選擇了。

  2. Even if there are no overloads, error messages generated at instantiation time usually expose users to confusing implementation details. For example, users might see references to names generated by BOOST_PARAMETER_FUNCTION such as graphs::detail::depth_first_search_with_named_params (or worse—think of the kinds of errors you get from your STL implementation when you make a mistake).4

    就算沒有函數重載,在實例化期間出現的錯誤信息通常暴露給用戶一些令人費解的實現細節。比如,用戶可能看到一些由 BOOST_PARAMETER_FUNCTION 生成的引用名,比如 graphs::detail::depth_first_search_with_named_params(或 者更糟糕——想想當你犯了個錯的時候,看到的那些STL實現所生成的錯誤信息)4

  3. The problems with exposing such permissive function template signatures have been the subject of much discussion, especially in the presence of unqualified calls. If all we want is to avoid unintentional argument-dependent lookup (ADL), we can isolate depth_first_search in a namespace containing no types6, but suppose we want it to found via ADL?

    這種暴露過於通用的函數模板簽名所帶來的問題已經成為了眾多討論的話題,特別是在 unqualified calls 出現之後。如果我們想要的只是避免這種無意的參數依賴查找(Argument-Dependent Lookup or ADL),我們可以將 depth_first_search 隔離在一個 不包含任何類型 6 的命名空間中,但是如果我們 希望 能通過 ADL 來查找呢?

It's usually a good idea to prevent functions from being considered for overload resolution when the passed argument types aren't appropriate. The library already does this when the required graph parameter is not supplied, but we're not likely to see a depth first search that doesn't take a graph to operate on. Suppose, instead, that we found a different depth first search algorithm that could work on graphs that don't model Incidence Graph? If we just added a simple overload, it would be ambiguous:

在傳入的參數不合適的時候,阻止函數被重載機制選擇到,這通常是一個好辦法。這個庫在沒有提供必需的 graph 參變量的時候已經這樣做了,只是我們不希望看到一個不對任何圖進行遍歷的深度優先搜索函 數。要是我們發現一個不同的深度優先搜索算法,它可以在不是 Incidence Graph 的圖上面遍歷呢?如果我們僅僅只是加入一個簡單的重載,這就會產生歧義。

// new overload 新的重載
BOOST_PARAMETER_FUNCTION(
(void), depth_first_search, (tag), (required (graph,*))( … ))
{
// new algorithm implementation 新算法的實現
}



// ambiguous! 歧義!
depth_first_search(boost::adjacency_list<>(), 2, "hello");
2.1.5.6.1   Adding Type Requirements 加入類型的約束信息

We really don't want the compiler to consider the original version of depth_first_search because the root_vertex argument, "hello", doesn't meet the requirement that it match the graph parameter's vertex descriptor type. Instead, this call should just invoke our new overload. To take the original depth_first_search overload out of contention, we need to tell the library about this requirement by replacing the * element of the signature with the required type, in parentheses:

我們真的不希望編譯器來考慮使用原始版本的 depth_first_search 函數,因為 root_vertex 參數 "hello" 不滿足 約束 條件(requirement)(這個約束條件匹配 graph 參變量的頂點描述(vertex descriptor)類型)。那麼,這個調用就應該只調用我們新的重載函數。為了讓編譯器不考慮使用原始版本的 depth_first_search 函數,我們需要將函數簽名中的星號元素(*)替 換成這個約束的類型(用圓括號括起來),來把這個約束信息告訴庫。

(root_vertex,
(typename boost::graph_traits<graph_type>::vertex_descriptor),
*vertices(graph).first)

Now the original depth_first_search will only be called when the root_vertex argument can be converted to the graph's vertex descriptor type, and our example that was ambiguous will smoothly call the new overload.

現在原始版本的 depth_first_search 函數只有在 root_vertex 參數可以轉換成圖的頂點描述(vertex descriptor)類型時才會被調用,並且我們那個 曾經 有歧義的例子現在也可以順利調用新的重載方法了。

Note 注意

The type of the graph argument is available in the signature—and in the function body—as graph_type. In general, to access the type of any parameter foo, write foo_type.
graph

參數的類型在函數簽名中是以 graph_type 類型存在的——在函數體中也是。一般地講,要獲得某個參變量 foo 的類型時,只需要寫 foo_type 就行了。

2.1.5.6.2   Predicate Requirements 謂詞約束

The requirements on other arguments are a bit more interesting than those on root_vertex; they can't be described in terms of simple type matching. Instead, they must be described in terms of MPL Metafunctions. There's no space to give a complete description of metafunctions or of graph library details here, but we'll show you the complete signature with maximal checking, just to give you a feel for how it's done. Each predicate metafunction is enclosed in parentheses and preceded by an asterix, as follows:

其他參數的類型約束信息比 root_vertex 的更有趣 一些;他們不能用簡單的類型匹配短語來描述。他們必須用 MPL Metafunctions 的短語來描述。這裡沒有篇幅來對 metafuncitons 以及 graph 庫的細節進行完整的描述,但是我們會給你看一個帶有最大參數檢查的完整函數簽名,讓你感受下它是如何做到的。每一個謂詞元函數(predicate metafunction)都被括在括號裡面並且在前面加一個星號,就像下面這樣:

BOOST_PARAMETER_FUNCTION(
(void), depth_first_search, graphs

, (required
(graph
, *(boost::mpl::and_<

boost::is_convertible<
boost::graph_traits<_>::traversal_category
, boost::incidence_graph_tag
>
, boost::is_convertible<
boost::graph_traits<_>::traversal_category
, boost::vertex_list_graph_tag
>
>)
))

(optional
(visitor, *, boost::dfs_visitor<>()) // not checkable 不可檢查的

(root_vertex
, (typename boost::graph_traits<graphs::graph::_>::vertex_descriptor)
, *vertices(graph).first)

(index_map
, *(boost::mpl::and_<

boost::is_integral<
boost::property_traits<_>::value_type
>
, boost::is_same<
typename boost::graph_traits<graphs::graph::_>::vertex_descriptor
, boost::property_traits<_>::key_type
>

>)

, get(boost::vertex_index,graph))
(in_out(color_map)
, *(boost::is_same<
typename boost::graph_traits<graphs::graph::_>::vertex_descriptor
, boost::property_traits<_>::key_type
>)

, default_color_map(num_vertices(graph), index_map) )
)
)

We acknowledge that this signature is pretty hairy looking. Fortunately, it usually isn't necessary to so completely encode the type requirements on arguments to generic functions. However, it is usally worth the effort to do so: your code will be more self-documenting and will often provide a better user experience. You'll also have an easier transition to an upcoming C++ standard with language support for concepts.

我們承認,這個函數簽名看起來非常的冗長。幸運的是,通常情況下沒有必要對泛型函數的參數寫如此完整的類型檢查。然而,它卻值得你去這 樣做:你的代碼將會 更加的一目瞭然(self-documenting)並且會給你帶來更好的用戶體驗。你也會有一個更加容易移植到即將到來的C++標準(語 言本身支持 concept (language support for concepts))。

2.1.5.7   Deduced Parameters 推導的參變量

To illustrate deduced parameter support we'll have to leave behind our example from the Graph library. Instead, consider the example of the def function from Boost.Python. Its signature is roughly as follows:

為了演示對推導參變量的支持,我們只好將 Graph 庫的例子先放一放。考慮 Boost.Python& nbsp;庫裡面的 def& nbsp;函數。它的函數簽名如下:

template <
class Function, Class KeywordExpression, class CallPolicies
>
void def(
// Required parameters
char const* name, Function func

// Optional, deduced parameters
, char const* docstring = ""
, KeywordExpression keywords = no_keywords()
, CallPolicies policies = default_call_policies()
);

Try not to be too distracted by the use of the term 「keywords」 in this example: although it means something analogous in Boost.Python to what it means in the Parameter library, for the purposes of this exercise you can think of it as being completely different.

不要被這個例子中的 「keywords」 弄迷糊了:儘管它在 Boost.Python 庫中也有著跟 Parameter 庫中像類似的含義,在這個例子中你可以把它們當成是完全不同的。

When calling def, only two arguments are required. The association between any additional arguments and their parameters can be determined by the types of the arguments actually passed, so the caller is neither required to remember argument positions or explicitly specify parameter names for those arguments. To generate this interface using BOOST_PARAMETER_FUNCTION, we need only enclose the deduced parameters in a (deduced …) clause, as follows:

在調用 def 時,只有兩個參數是必需的。任何其他參數和相應的參變量都可以通過傳入參數的類型來對應上,因此調用者既不需要記住參數的位置,也不需要顯式的給出參數相 應參變量的名字。要使用 BOOST_PARAMETER_FUNCTION& nbsp;來生成這個接口,我們只需要將推導參變量括在一個 (deduced …) 語句中,就 像下面這樣:

namespace mpl = boost::mpl;

BOOST_PARAMETER_FUNCTION(
(void), def, tag,

(required (name,(char const*)) (func,*) ) // nondeduced 不可推導的

(deduced
(optional
(docstring, (char const*), "")
(keywords
, *(is_keyword_expression<mpl::_>) // 見5
, no_keywords())
(policies
, *(mpl::not_<
mpl::or_<
boost::is_convertible<mpl::_, char const*>
, is_keyword_expression<mpl::_> // 見5
>
>)
, default_call_policies()
)
)
)
)
{

}

Syntax Note 語法注意事項

A (deduced …) clause always contains a ``(required …)`` and/or an ``(optional …)`` subclause, and must follow any (required …) or ``(optional …)`` clauses indicating nondeduced parameters at the outer level.

推導語句 (deduced …)  總是包含一個 (required
…)
和/或一個 (optional
…)
子語句,並且必須跟在外面一層表示非推導參變量的所有 (required
…)
 或 (optional
…)
語句的後面。

With the declaration above, the following two calls are equivalent:

使用上面的聲明,下面兩個調用是等價的:

def("f", &f, some_policies, "Documentation for f");
def("f", &f, "Documentation for f", some_policies);

If the user wants to pass a policies argument that was also, for some reason, convertible to char const*, she can always specify the parameter name explicitly, as follows:

如果用戶想傳遞一個因某種原因可以轉化為 char const*policies 參數,她可以像下面這樣顯式的指定參變量的名字:

def(
"f", &f
, _policies = some_policies, "Documentation for f");

2.2   Parameter-Enabled Member Functions 允許參變量的成員函數

The BOOST_PARAMETER_MEMBER_FUNCTION and BOOST_PARAMETER_CONST_MEMBER_FUNCTION macros accept exactly the same arguments as BOOST_PARAMETER_FUNCTION, but are designed to be used within the body of a class:

BOOST_PARAMETER_MEMBER_FUNCTION 宏 和 BOOST_PARAMETER_CONST_MEMBER_FUNCTION 宏接受 跟 BOOST_PARAMETER_FUNCTION 宏完全一樣的參數,只是它們是被設計來用在類裡面的:

BOOST_PARAMETER_NAME(arg1)
BOOST_PARAMETER_NAME(arg2)

struct callable2
{
BOOST_PARAMETER_CONST_MEMBER_FUNCTION(
(void), operator(), tag, (required (arg1,(int))(arg2,(int))))
{
std::cout << arg1 << ", " << arg2 << std::endl;
}
};

These macros don't directly allow a function's interface to be separated from its implementation, but you can always forward arguments on to a separate implementation function:

這些宏不能直接地允許一個函數的接口同它的實現相分離,但是你可以將這些參數傳遞到一個單獨的實現函數:

struct callable2
{
BOOST_PARAMETER_CONST_MEMBER_FUNCTION(
(void), operator(), tag, (required (arg1,(int))(arg2,(int))))
{
call_impl(arg1,arg2);
}
private:
void call_impl(int, int); // implemented elsewhere. 在別的地方實現
};

2.3   Parameter-Enabled Constructors 參變量允許的構造函數

The lack of a 「delegating constructor」 feature in C++ (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf) limits somewhat the quality of interface this library can provide for defining parameter-enabled constructors. The usual workaround for a lack of constructor delegation applies: one must factor the common logic into a base class.

由於C++缺少「代理構造(delegating constructor)」特性 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf) ,這在某種程度上限制了這個庫接能提供的參變量允許的構造函數(parameter-enabled constructor)的接口質量。對於缺少構造函數代理機制的情形來說,通常的工作轉移是這樣的: 你必須將公共的邏輯轉移到基類中去。

Let's build a parameter-enabled constructor that simply prints its arguments. The first step is to write a base class whose constructor accepts a single argument known as an ArgumentPack: a bundle of references to the actual arguments, tagged with their keywords. The values of the actual arguments are extracted from the ArgumentPack by indexing it with keyword objects:

我們先構建一個參數允許的構造函數(parameter-enabled constructor),它只是簡單的打印出它的參數。第一步就是寫一個基類,它的構造函數 接受一個單獨的參數,我們把這個參數叫做參 數包(ArgumentPack): 一組對實際參數的引用,它們用各自的關鍵字做過標記(tagged)。 真實參數的值可以通過用關鍵字對像做為索引(index)參數包(ArgumentPack)中獲得。

BOOST_PARAMETER_NAME(name)
BOOST_PARAMETER_NAME(index)

struct myclass_impl
{
template <class ArgumentPack>
myclass_impl(ArgumentPack const& args)
{
std::cout << "name = " << args[_name]
<< "; index = " << args[_index | 42]
<< std::endl;
}
};

Note that the bitwise or (「|」) operator has a special meaning when applied to keyword objects that are passed to an ArgumentPack's indexing operator: it is used to indicate a default value. In this case if there is no index parameter in the ArgumentPack, 42 will be used instead.

注意這裡的按位或運算符(「|」)在被用到傳 給參數包(ArgumentPack)作為索引 (index)的關鍵字對像上時有它特殊的含義:它表示默認值。 這個例子中,如果在ArgumentPack中沒有index 參變量,42將 會被作為默認值使用。

Now we are ready to write the parameter-enabled constructor interface:

現在我們已經寫了一個參數允許的構造函數(parameter-enabled constructor)接口:

struct myclass : myclass_impl
{
BOOST_PARAMETER_CONSTRUCTOR(
myclass, (myclass_impl), tag
, (required (name,*)) (optional (index,*))) // no semicolon 沒有分號
};

Since we have supplied a default value for index but not for name, only name is required. We can exercise our new interface as follows:

我們已經為index提供了一個默認值,但是沒有為 name提 供默認值,只有name 是必需的。我們像下面這樣練習我們的新接口:

myclass x("bob", 3); // positional 位置相關的
myclass y(_index = 12, _name = "sally"); // named 有名的
myclass z("june"); // positional/defaulted 位置相關的或者默認的

For more on ArgumentPack manipulation, see the Advanced Topics section.

關於參數包(ArgumentPack)更 多的信息,請參考高 級話題(Advanced Topics)這一節。

2.4   Parameter-Enabled Class Templates 參數允許的類模板

In this section we'll use Boost.Parameter to build Boost.Python's class_ template, whose 「signature」 is:

在這一節裡面,我們將使用 Boost.Parameter 構建一個 Boost.Pythonclass_類 模板,它的簽名(signature)如下:

template class<
ValueType, BaseList = bases<>
, HeldType = ValueType, Copyable = void
>
class class_;

Only the first argument, ValueType, is required.

只有第一個參數,ValueType是必需的。

2.4.1   Named Template Parameters 有名模板參變量

First, we'll build an interface that allows users to pass arguments positionally or by name:

首先,我們構建一個允許用戶通過位置或者名字傳遞參數的接口:

struct B { virtual ~B() = 0; };
struct D : B { ~D(); };

class_<
class_type<B>, copyable<boost::noncopyable>
> …;
class_<
D, held_type<std::auto_ptr<D> >, base_list<bases<B> >
> …;

2.4.1.1   Template Keywords 模板關鍵字

The first step is to define keywords for each template parameter:

第一步是為每個模板參變量定義關鍵字:

namespace boost { namespace python {

BOOST_PARAMETER_TEMPLATE_KEYWORD(class_type)
BOOST_PARAMETER_TEMPLATE_KEYWORD(base_list)
BOOST_PARAMETER_TEMPLATE_KEYWORD(held_type)
BOOST_PARAMETER_TEMPLATE_KEYWORD(copyable)

}}

The declaration of the class_type keyword you see here is equivalent to:

這裡你看到的class_type關鍵字聲明等價於:

namespace boost { namespace python {
namespace tag { struct class_type; } // keyword tag type
template <class T>
struct class_type
: parameter::template_keyword<tag::class_type,T>
{};
}}

It defines a keyword tag type named tag::class_type and a parameter passing template named class_type.

這裡定義了一個叫做 tag::class_type 的關鍵字標籤類型(keyword tag type) 和一個叫做 class_type參數傳遞模板(parameter passing template)

2.4.1.2   Class Template Skeleton 類模板骨架

The next step is to define the skeleton of our class template, which has three optional parameters. Because the user may pass arguments in any order, we don't know the actual identities of these parameters, so it would be premature to use descriptive names or write out the actual default values for any of them. Instead, we'll give them generic names and use the special type boost::parameter::void_ as a default:

下一步就是定義我們的類模板骨架,它有三個可選參數。因為用戶可能以任何順序傳遞參數,我們不需要知道這些參變量的真實標識符 (identity), 因此為它們使用描述性的名字或者寫出真實的默認值是不可取的。取而代之,我們將給他們一些通用的名字並使用一個特殊的 類型boost::parameter::void_作 為默認值:

namespace boost { namespace python {

template <
class A0
, class A1 = parameter::void_
, class A2 = parameter::void_
, class A3 = parameter::void_
>
struct class_
{

};
}}

2.4.1.3   Class Template Signatures 類模板簽名(Signature)

Next, we need to build a type, known as a ParameterSpec, describing the 「signature」 of boost::python::class_. A ParameterSpec enumerates the required and optional parameters in their positional order, along with any type requirements (note that it does not specify defaults -- those will be dealt with separately):

接下來我們構建一個叫做ParameterSpec的 類型, 用來描述boost::python::class_類 的「簽名(signature)」。 ParameterSpec將以位置順序枚舉必 需的和可選的參變量, 也包括所有的類型約束(type requirements)(注意它並不關注默認值——它們將被單獨處理):

namespace boost { namespace python {

using boost::mpl::_;

typedef parameter::parameters<
required<tag::class_type, is_class<_> >
, optional<tag::base_list, mpl::is_sequence<_> >
, optional<tag::held_type>

, optional<tag::copyable>
> class_signature;

}}

2.4.1.4   Argument Packs and Parameter Extraction 參數包(Argument Packs)和參變量(Parameter)提取

Next, within the body of class_ , we use the ParameterSpec's nested ::bind< > template to bundle the actual arguments into an ArgumentPack type, and then use the library's binding< > metafunction to extract 「logical parameters」. Note that defaults are specified by supplying an optional third argument to binding< >:

接下來,在class_的定義體中,我們使用ParameterSpec內部 的 ::bind< >模 板將 真實的參數綁定到一個ArgumentPack類 型,並且使用庫中的 binding< >元 函數 (metafunction)來提取「邏輯參變量(logical parameters)」。注意,默認值是通過在binding< >中 提供可選的第三個參數來給定的:

namespace boost { namespace python {

template <
class A0
, class A1 = parameter::void_
, class A2 = parameter::void_
, class A3 = parameter::void_
>
struct class_
{
// Create ArgumentPack
typedef typename
class_signature::bind<A0,A1,A2,A3>::type
args;

// Extract first logical parameter.
typedef typename parameter::binding<

args, tag::class_type>::type class_type;

typedef typename parameter::binding<
args, tag::base_list, bases<> >::type base_list;

typedef typename parameter::binding<
args, tag::held_type, class_type>::type held_type;

typedef typename parameter::binding<
args, tag::copyable, void>::type copyable;
};

}}

2.4.2   Exercising the Code So Far 練習

Revisiting our original examples,

複習我們最開始的例子,

typedef boost::python::class_<
class_type<B>, copyable<boost::noncopyable>
> c1;

typedef boost::python::class_<
D, held_type<std::auto_ptr<D> >, base_list<bases<B> >
> c2;

we can now examine the intended parameters:

我們現在可以檢驗參變量的預期值:

BOOST_MPL_ASSERT((boost::is_same<c1::class_type, B>));
BOOST_MPL_ASSERT((boost::is_same<c1::base_list, bases<> >));
BOOST_MPL_ASSERT((boost::is_same<c1::held_type, B>));
BOOST_MPL_ASSERT((
boost::is_same<c1::copyable, boost::noncopyable>
));

BOOST_MPL_ASSERT((boost::is_same<c2::class_type, D>));
BOOST_MPL_ASSERT((boost::is_same<c2::base_list, bases<B> >));
BOOST_MPL_ASSERT((
boost::is_same<c2::held_type, std::auto_ptr<D> >

));
BOOST_MPL_ASSERT((boost::is_same<c2::copyable, void>));

2.4.3   Deduced Template Parameters 推導的模板參變量

To apply a deduced parameter interface here, we need only make the type requirements a bit tighter so the held_type and copyable parameters can be crisply distinguished from the others. Boost.Python does this by requiring that base_list be a specialization of its bases< > template (as opposed to being any old MPL sequence) and by requiring that copyable, if explicitly supplied, be boost::noncopyable. One easy way of identifying specializations of bases< > is to derive them all from the same class, as an implementation detail:

為了在這裡使用推導的參變量接口,我們只需要讓類型約束的條件更嚴格一些,這樣 held_typecopyable 參變量就可以清晰地和其他的參變量區分開來。 Boost.Python庫 做到這一點,它要求 base_listbases< >的 一個偏特化模板 (這和一些舊的MPL序列相對應)並且要求copyableboost::noncopyable(如 果顯式提供的話)。一種簡單的識別bases< >偏特化的方式是讓它 們都從同一個基類集成,實現細節:

namespace boost { namespace python {

namespace detail { struct bases_base {}; }

template <class A0 = void, class A1 = void, class A2 = void >
struct bases : detail::bases_base
{};
}}

Now we can rewrite our signature to make all three optional parameters deducible:

現在我們可以重寫我們的函數簽名,使得所有的三個可選參變量都是可推導的:

typedef parameter::parameters<

required<tag::class_type, is_class<_> >

, optional<
deduced<tag::base_list>
, is_base_and_derived<detail::bases_base,_>

>

, optional<
deduced<tag::held_type>
, mpl::not_<
mpl::or_<
is_base_and_derived<detail::bases_base,_>

, is_same<noncopyable,_>
>
>
>

, optional<deduced<tag::copyable>, is_same<noncopyable,_> >

> class_signature;

It may seem like we've added a great deal of complexity, but the benefits to our users are greater. Our original examples can now be written without explicit parameter names:

看上去似乎我們加入了大量的複雜性,但是給用戶帶來的便利卻更大。我們最開始的例子現在可以不需要顯式的參變量名了,重寫如下:

typedef boost::python::class_<B, boost::noncopyable> c1;

typedef boost::python::class_<D, std::auto_ptr<D>, bases<B> > c2;

3   Advanced Topics 高級話題

At this point, you should have a good grasp of the basics. In this section we'll cover some more esoteric uses of the library.

到目前為止,你應該很好地掌握了基本知識。在這一節中,我們將講到這個庫的一些更加細節的使用。

3.1   Fine-Grained Name Control 細粒度的名字控制

If you don't like the leading-underscore naming convention used to refer to keyword objects, or you need the name tag for something other than the keyword type namespace, there's another way to use BOOST_PARAMETER_NAME:

如果你不喜歡使用下劃線開頭的命名規範來指代關鍵字對象,或者你想要tag 這個名字表示其他的東西,而不是關鍵字類型的名字空間,也又另外一種使用BOOST_PARAMETER_NAME宏的方法:

BOOST_PARAMETER_NAME((object-name, tag-namespace) parameter-name)

Here is a usage example:

這裡是使用的例子:

BOOST_PARAMETER_NAME((pass_foo, keywords) foo)

BOOST_PARAMETER_FUNCTION(
(int), f,
keywords, (required (foo, *)))
{
return foo + 1;
}

int x = f(pass_foo = 41);

Before you use this more verbose form, however, please read the section on best practices for keyword object naming.

在你使用更細節的形式之前,請讀下關於 關鍵字對像命名最 佳實踐(best practices for keyword object naming) 的章節。

3.2   More ArgumentPacks 更多關於參數包(ArgumentPack)

We've already seen ArgumentPacks when we looked at parameter-enabled constructors and class templates. As you might have guessed, ArgumentPacks actually lie at the heart of everything this library does; in this section we'll examine ways to build and manipulate them more effectively.

在我們查看 參變量允許的構造函數(parameter- enabled constructors)類模板(class templates) 章節時,我們已經瞭解過參數包(ArgumentPack)。 你可能已經猜到了,參數包(ArgumentPack) 事實上就是這個類所做一切的核心;在這一節中,我們將講解一些構建以及更有效操作它們的不同方法。

3.2.1   Building ArgumentPacks 構建參數包(ArgumentPack)

The simplest ArgumentPack is the result of assigning into a keyword object:

最簡單的參數包(ArgumentPack)就 是給一個關鍵字對像賦值:

BOOST_PARAMETER_NAME(index)

template <class ArgumentPack>
int print_index(ArgumentPack const& args)
{
std::cout << "index = " << args[_index] << std::endl;
return 0;
}

int x = print_index(_index = 3); // prints "index = 3"

Also, ArgumentPacks can be composed using the comma operator. The extra parentheses below are used to prevent the compiler from seeing two separate arguments to print_name_and_index:

參數包(ArgumentPack)也 可以是逗號分割的(元素)組合。下面多餘的圓括號是用來防止編譯器認為 傳入了兩個參數給 print_name_and_index 函數:

BOOST_PARAMETER_NAME(name)

template <class ArgumentPack>
int print_name_and_index(ArgumentPack const& args)
{
std::cout << "name = " << args[_name] << "; ";
return print_index(args);
}

int y = print_name_and_index((_index = 3, _name = "jones"));

To build an ArgumentPack with positional arguments, we can use a ParameterSpec. As introduced described in the section on Class Template Signatures, a ParameterSpec describes the positional order of parameters and any associated type requirements. Just as we can build an ArgumentPack type with its nested ::bind< > template, we can build an ArgumentPack object by invoking its function call operator:

為了構建帶有位置相關參數(positional arguments)的參 數包(ArgumentPack), 我們使用ParameterSpec。 在 類 模板簽名(Class Template Signatures) 這一節中介紹過, ParameterSpec 描述了參變量的位置順序以及與它們相關的類型約束信息。就像我們可以使用內嵌 的::bind< > 來構建 參數包(ArgumentPack) 一樣,我們也可以通過調用它的仿函數操作符來構建一個 參數包(ArgumentPack) 對 象(object)

parameter::parameters<
required<tag::name, is_convertible<_,char const*> >
, optional<tag::index, is_convertible<_,int> >
> spec;

char const sam[] = "sam";
int twelve = 12;

int z0 = print_name_and_index( spec(sam, twelve) );

int z1 = print_name_and_index(
spec(_index=12, _name="sam")
);

Note that because of the forwarding problem, parameter::parameters::operator() can't accept non-const rvalues.

注意,由於 轉 接問題(forwarding problem), 仿函數操作符 parameter::parameters::operator() 不能接受非常量(non-const)的右值(rvalue)。

3.2.2   Extracting Parameter Types 提取參變量類型

If we want to know the types of the arguments passed to print_name_and_index, we have a couple of options. The simplest and least error-prone approach is to forward them to a function template and allow it to do type deduction:

如果我們希望知道傳遞給 print_name_and_index 函數的參數的類型信息, 我們有很多的方法。最簡單並且最不容易出錯的辦法就是將他們轉交給一個函數模板,並且允許做類型推導 (type deduction):

BOOST_PARAMETER_NAME(name)
BOOST_PARAMETER_NAME(index)

template <class Name, class Index>
int deduce_arg_types_impl(Name& name, Index& index)
{
Name& n2 = name; // we know the types 我們知道了類型
Index& i2 = index;
return index;
}

template <class ArgumentPack>
int deduce_arg_types(ArgumentPack const& args)
{
return deduce_arg_types_impl(args[_name], args[_index|42]);
}

Occasionally one needs to deduce argument types without an extra layer of function call. For example, suppose we wanted to return twice the value of the index parameter? In that case we can use the binding< > metafunction introduced earlier:

有時候,我們需要推導函數的類型信息而不引入額外的函數調用。比如,假如我們希望返回參變量 index 值的兩倍呢? 在這種情況下,我們可以使用在 早 些時候 介紹過的 binding< > 元函數(metafunction):

BOOST_PARAMETER_NAME(index)

template <class ArgumentPack>
typename remove_reference<
typename parameter::binding<ArgumentPack, tag::index, int>::type
>::type
twice_index(ArgumentPack const& args)
{
return 2 * args[_index|42];
}

int six = twice_index(_index = 3);

Note that the remove_reference< > dance is necessary because binding< > will return a reference type when the argument is bound in the argument pack. If we don't strip the reference we end up returning a reference to the temporary created in the 2*… expression. A convenient shortcut would be to use the value_type< > metafunction:

注意,這裡 remove_reference< > 的技巧 是必要的,因為當參數在參數包(ArgumentPack)裡面的 時候 binding< > 會 返回一個引用的類型。如果我們不將這個引用去除掉,我們最後就會返回一個指向由 2*… 表達式創建的臨時值的引用。 一種方便的簡單方式就是使用 value_type< > 元函數(metafunction):

template <class ArgumentPack>
typename parameter::value_type<ArgumentPack, tag::index, int>::type
twice_index(ArgumentPack const& args)
{
return 2 * args[_index|42];
}

3.2.3   Lazy Default Computation 延遲的默認值計算

When a default value is expensive to compute, it would be preferable to avoid it until we're sure it's absolutely necessary. BOOST_PARAMETER_FUNCTION takes care of that problem for us, but when using ArgumentPacks explicitly, we need a tool other than operator|:

當一個默認值表達式可以被計算的時候,最好是等到我們確定真的需要它的時候才進行計算。 宏 BOOST_PARAMETER_FUNCTION 已經為我們解決了這個問題, 但是在顯式使用 ArgumentPack 時,我們需要一個工具而不僅僅是 "|"操作符

BOOST_PARAMETER_NAME(s1)
BOOST_PARAMETER_NAME(s2)
BOOST_PARAMETER_NAME(s3)

template <class ArgumentPack>
std::string f(ArgumentPack const& args)
{
std::string const& s1 = args[_s1];
std::string const& s2 = args[_s2];
typename parameter::binding<
ArgumentPack,tag::s3,std::string
>::type s3 = args[_s3|(s1+s2)]; // always constructs s1+s2 總是會構造 s1+s2
return s3;
}

std::string x = f((_s1="hello,", _s2=" world", _s3="hi world"));

In the example above, the string "hello, world" is constructed despite the fact that the user passed us a value for s3. To remedy that, we can compute the default value lazily (that is, only on demand), by using boost::bind() to create a function object.

在上面的這個例子中,即使用戶給 s3 傳入了一個 值,"hello, world" 字符串 也會被構造出來。為了彌補這個不足,我們可以通過使用 boost::bind() 創建 一個函數對像來 延遲地(lazily) 計算它的值(也就是說只有在需要使用到的時候)。

using boost::bind;
using boost::ref;

typename parameter::binding<
ArgumentPack, tag::s3, std::string
>::type s3 = args[_s3 || bind(std::plus<std::string>(), ref(s1), ref(s2)) ];

The expression bind(std::plus<std::string>(), ref(s1), ref(s2)) yields a function object that, when invoked, adds the two strings together. That function will only be invoked if no s3 argument is supplied by the caller.

表達式 bind(std::plus<std::string>(), ref(s1), ref(s2)) 生成 了一個函數對像(function object),當被調用的時候,將這兩個字符串連接在一起。這個函數只 有在用戶沒有傳入 s3 參數 的時候才會被調用。

4   Best Practices 最佳實踐

By now you should have a fairly good idea of how to use the Parameter library. This section points out a few more-marginal issues that will help you use the library more effectively.

到目前為止,你應該對如何使用 Parameter 庫有了很好的掌握。這一節會指出一些更加邊緣的問題,這會幫助你更高效地使用這個庫。

4.1   Keyword Naming 關鍵字命名

BOOST_PARAMETER_NAME prepends a leading underscore to the names of all our keyword objects in order to avoid the following usually-silent bug:

BOOST_PARAMETER_NAME 會在關鍵字對象的名字前面加入一個下劃線 來避免下面這樣的通常悄無聲息的臭蟲(bug):

namespace people
{
namespace tag { struct name; struct age; }

namespace // unnamed 無名的
{
boost::parameter::keyword<tag::name>& name
= boost::parameter::keyword<tag::name>::instance;
boost::parameter::keyword<tag::age>& age
= boost::parameter::keyword<tag::age>::instance;
}
BOOST_PARAMETER_FUNCTION(
(void), g, tag, (optional (name, *, "bob")(age, *, 42)))
{
std::cout << name << ":" << age;
}
void f(int age)
{
.

g(age = 3); // whoops! 糟糕!
}
}

Although in the case above, the user was trying to pass the value 3 as the age parameter to g, what happened instead was that f's age argument got reassigned the value 3, and was then passed as a positional argument to g. Since g's first positional parameter is name, the default value for age is used, and g prints 3:42. Our leading underscore naming convention that makes this problem less likely to occur.

在上面的這個例子中,儘管用戶嘗試著給函數 gage 參 變量傳入 3 這個值,可是最終的發生的事情卻是給函數 f 的 參數 age 重新賦值到了 3 這個值,並且 接著作為一個位置相關參數(positional argument)傳給了函數 g。由於 函數 g 的第一個位置相關(positional)參數是 name, 參變量 age 就會使用它的默認值,這樣函數 g 就會打印 出 3:42。 我們的下劃線開頭的命名規範就可以減少這種問題發生的可能性。

In this particular case, the problem could have been detected if f's age parameter had been made const, which is always a good idea whenever possible. Finally, we recommend that you use an enclosing namespace for all your code, but particularly for names with leading underscores. If we were to leave out the people namespace above, names in the global namespace beginning with leading underscores—which are reserved to your C++ compiler—might become irretrievably ambiguous with those in our unnamed namespace.

在這個特殊情況下,如果函數 fage 參變量 被聲明為 const 的(這在任何可能的時候都是一個好的辦法),這樣的問題可能已經被檢測到了。 最後,我們建議你把你所有的代碼包裝在一個名字空間中,特別是對那些帶有下劃線開頭的名字。如果我們離開了上面 的 people 名字空間,那些在全局名字空間中以下劃線開頭的名字——這樣的名字是保留給C++編譯器的——可能和我們 的無名名字空間中的名字相混淆。

4.2   Namespaces 名字空間

In our examples we've always declared keyword objects in (an unnamed namespace within) the same namespace as the Boost.Parameter-enabled functions using those keywords:

在我們的例子中,我們總是將關鍵字對像聲明在(以無名名字空間的形式)使用它們的參變量允許的(Boost.Parameter- enabled)函數所在的名字空間:

namespace lib
{
BOOST_PARAMETER_NAME(name)
BOOST_PARAMETER_NAME(index)

BOOST_PARAMETER_FUNCTION(
(int), f, tag,
(optional (name,*,"bob")(index,(int),1))
)
{
std::cout << name << ":" << index << std::endl;
return index;
}
}

Users of these functions have a few choices:

使用這些函數的用戶有以下幾種選擇:

  1. Full qualification: 全名稱:
int x = lib::f(lib::_name = "jill", lib::_index = 1);

This approach is more verbose than many users would like.

這種方式太繁瑣了,大多數用戶都不喜歡這樣。

  1. Make keyword objects available through using-declarations:

    通過使用 using-declarations 來使關鍵字對象可用:

using lib::_name;
using lib::_index;

int x = lib::f(_name = "jill", _index = 1);

This version is much better at the actual call site, but the using-declarations themselves can be verbose and hard-to manage.

這個版本在實際使用的時候要好多了,但是 using-declarations 本身也可能變得很繁瑣,並且難以維護。

  1. Bring in the entire namespace with a using-directive:

    使用 using-directive 來引入整個名字空間:

using namespace lib;
int x = f(_name = "jill", _index = 3);

This option is convenient, but it indiscriminately makes the entire contents of lib available without qualification.

這個方法很方便,但是它卻不加選擇地將整個 lib 名字空間中的內容導入進來。

If we add an additional namespace around keyword declarations, though, we can give users more control:

如果我們在關鍵字聲明的外面加入一個額外的名字空間,我們就可以給用戶更多的控制方式:

namespace lib
{
namespace keywords
{

BOOST_PARAMETER_NAME(name)
BOOST_PARAMETER_NAME(index)
}
BOOST_PARAMETER_FUNCTION(
(int), f, keywords::tag,
(optional (name,*,"bob")(index,(int),1))
)
{
std::cout << name << ":" << index << std::endl;
return index;
}
}

Now users need only a single using-directive to bring in just the names of all keywords associated with lib:

現在用戶只需要一個單獨的 using-directive 就可以將所有與 lib 相關的 名字關鍵字對像導入了。

using namespace lib::keywords;
int y = lib::f(_name = "bob", _index = 2);

4.3   Documentation 文檔

The interface idioms enabled by Boost.Parameter are completely new (to C++), and as such are not served by pre-existing documentation conventions.

Boost.Parameter 庫所創造的接口風格對C++來說是全新的,並且因此不能沿用已有的文檔規範。

Note 注意

This space is empty because we haven't settled on any best practices yet. We'd be very pleased to link to your documentation if you've got a style that you think is worth sharing.

這一小節是留空的,因為我們還沒有找到一個最好的實踐方式。如果你有一種你認為值得分享的文檔樣式,我們非常樂意鏈接到你的文檔。

5   Portability Considerations 可移植性的考慮

Use the regression test results for the latest Boost release of the Parameter library to see how it fares on your favorite compiler. Additionally, you may need to be aware of the following issues and workarounds for particular compilers.

利用 regression 測試結果 來查看 Parameter 庫最新的 Boost 發佈版 能多好的支持你最喜歡的編譯器。進一步講,你可能需要注意下面這些問題以及對特定編譯器的一些迂迴工作。

5.1   No SFINAE Support 不支持 SFINAE

Some older compilers don't support SFINAE. If your compiler meets that criterion, then Boost headers will #define the preprocessor symbol BOOST_NO_SFINAE, and parameter-enabled functions won't be removed from the overload set based on their signatures.

一些老的編譯器不支持 SFINAE。如果你的編譯器有這個問題,Boost 的頭文件就會 定義
(#define)
一個 預處理符號 BOOST_NO_SFINAE, 並且參變量允許的函數(parameter-enabled function)將不會根據它們的簽名 從重載集(overload set)中被移除。

5.2   No Support for 不支持 result_of

Lazy default computation relies on the result_of class template to compute the types of default arguments given the type of the function object that constructs them. On compilers that don't support result_of, BOOST_NO_RESULT_OF will be #defined, and the compiler will expect the function object to contain a nested type name, result_type, that indicates its return type when invoked without arguments. To use an ordinary function as a default generator on those compilers, you'll need to wrap it in a class that provides result_type as a typedef and invokes the function via its operator().

延 遲的默認值計算(Lazy default computation) 是依賴 於 result_of 類模板來計算默認參數值(給定了創建它們的函數對象的類型)的。 在那些不支持 result_of 的編譯器上面,BOOST_NO_RESULT_OF 宏 就會被
義(#define)
,並且編譯器會期望這個函數對像包含一個內嵌的類型 名 result_type, 它表示不帶參數被調用時的返回值類型。

5.3   Compiler Can't See References In Unnamed Namespace 編譯器看不見無名空間裡面的引用

If you use Microsoft Visual C++ 6.x, you may find that the compiler has trouble finding your keyword objects. This problem has been observed, but only on this one compiler, and it disappeared as the test code evolved, so we suggest you use it only as a last resort rather than as a preventative measure. The solution is to add using-declarations to force the names to be available in the enclosing namespace without qualification:

如果你使用 Microsoft Visual C++ 6.x,你可能會發現編譯器在查找你的關鍵字對象的時候遇到了困難。這個問題已經被觀察到了,但是只在這一個編譯器上面, 並且在測試代碼更新之後這個問題消失了,因此我們建議你將其作為最後一個手段而不是預先的方式進行使用。解決方法就是加入 using-declarations 來 強制使這些名字在其所在的名字空間中無條件的可用。

namespace graphs
{
using graphs::graph;
using graphs::visitor;
using graphs::root_vertex;
using graphs::index_map;
using graphs::color_map;
}

6   Python Binding Python 綁定

Follow this link for documentation on how to expose Boost.Parameter-enabled functions to Python with Boost.Python.

查看 這 個鏈接 指向的文檔來找到如何使 用 Boost.Python 庫暴露參變量允許的函數(Boost.Parameter-enabled function)給 Python。

7   Reference 參考文檔

Follow this link to the Boost.Parameter reference documentation.

這 裡 查看 Boost.Parameter 的參考文檔。

8   Glossary 詞彙表

Argument (or 「actual argument」): 參數(Argument)(或者 「實參(actual argument)」):
 

the value actually passed to a function or class template

傳遞給一個函數或者類模板的實際值

Parameter (or 「formal parameter」): 參變量(Parameter)(或者 「形參(formal parameter)」)
 

the name used to refer to an argument within a function or class template. For example, the value of f's parameter x is given by the argument 3:

用來在函數或者類模板裡面引用參數(argument)的名字。比如說,函數 f參變量 (Parameter) 的值就是由 參數(argument) 3 給定的:

int f(int x) { return x + 1 }
int y = f(3);

9   Acknowledgements 致謝

The authors would like to thank all the Boosters who participated in the review of this library and its documentation, most especially our review manager, Doug Gregor.

本庫的作者在這裡要感謝所有參與庫的評審和文檔撰寫工作的Boost人(Booster),特別要感謝我們的評審負責人 Doug Gregor。


[1]

As of Boost 1.33.0 the Graph library was still using an older named parameter mechanism, but there are plans to change it to use Boost.Parameter (this library) in an upcoming release, while keeping the old interface available for backward-compatibility.

我們知道 Boost 1.33.0 的 Graph 庫仍然在使 用 舊式的參數命名機制 (older named parameter mechanism),並且 計劃在即將到來的發行版中將他們改為使用 Boost.Parameter 庫(本庫),同時為了後向兼容性(backward-compatibility),舊的接口會保持不變。

[2]

The One Definition Rule says that any given entity in a C++ program must have the same definition in all translation units (object files) that make up a program.

一處定義規則(One Definition Rule) 說的是,在一個 C++ 程序中任何一個給定的實體在組成這個程序的 所有的翻譯單元(translation unit)(或者目標文件(object file))中都必須有相同的定義。

[3]

If you're not familiar with the Boost Graph Library, don't worry about the meaning of any Graph-library-specific details you encounter. In this case you could replace all mentions of vertex descriptor types with int in the text, and your understanding of the Parameter library wouldn't suffer.

如果你不熟悉 Boost Graph 庫,不用擔心你碰到的任何 Graph 庫相關的細節。在這裡,你可以將文本中提到的所有頂點描述符類型(vertex descriptor type)替換 成 int, 這樣你理解起 Parameter 庫來就不會有困難了。

[4]

This is a major motivation behind ConceptC++.

這是 ConceptC++ 背後的一個主要動機。

[5]

(1, 2) Here we're assuming there's a predicate metafunction is_keyword_expression that can be used to identify models of Boost.Python's KeywordExpression concept.

(1, 2) 這裡,我們假設存在一個謂詞元函數(predicate metafunction) is_keyword_expression, 它可以用來標識 Boost.Python 的 KeywordExpression 概念模型。

[6]

You can always give the illusion that the function lives in an outer namespace by applying a using-declaration:

你可以使用 using-declaration 來表示這個函數是存在於一個外部的命名空間中的:

 namespace foo_overloads
{
// foo declarations here
void foo() { ... }
...
}
using foo_overloads::foo;

This technique for avoiding unintentional argument-dependent lookup is due to Herb Sutter.
這個避免無意的參數依賴查找(Argument-Dependent Lookup)技術是由 Herb Sutter 提出的。
[7]

This capability depends on your compiler's support for SFINAE. SFINAE: Substitution Failure Is Not An Error. If type substitution during the instantiation of a function template results in an invalid type, no compilation error is emitted; instead the overload is removed from the overload set. By producing an invalid type in the function signature depending on the result of some condition, we can decide whether or not an overload is considered during overload resolution. The technique is formalized in the enable_if utility. Most recent compilers support SFINAE; on compilers that don't support it, the Boost config library will #define the symbol BOOST_NO_SFINAE. See http://www.semantics.org/once_weakly/w02_SFINAE.pdf for more information on SFINAE.

這種能力取決於你到編譯器是否支持 SFINAE。替換失敗不是一種錯 誤(Substitution Failure Is Not An Error)。 如果在實例化函數模板的過程中,類型替換導致了一個不正確的類型,編譯器不是報出編譯錯誤;而是將這個重載函數(overload)移出重載集 (overload set)。 通過在依賴某些條件結果的函數簽名中生成一個不正確的類型,我們可以決定一個重載函數(overload)在重載解析(overload resolution)的過程中是否會被 考慮到(譯註:加入到重載集(overload set)中去)。