![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
The Boost Parameter LibraryBoost , ,
|
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. |
---|
Authors: | David Abrahams, Daniel Wallin | |||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Contact: | reference documentation
Table of Contents 1 MotivationВ C++,аргументыобычно имеют значение по их позициям относительно спискапараметров: первый аргумент передал карты на первый параметр в определении функции и так далее. Этот протокол хорош, когда есть хотя бы один параметр со значением по умолчанию, но когда есть даже несколько полезных по умолчанию, позиционный интерфейс становится обременительным:
1.1 Named Function ParametersThis 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 ParametersA 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: 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 SupportThe 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: 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: // p and q could be equivalent, given a deduced // parameter interface. smart_ptr<shared, Client> p; smart_ptr<Client, shared> q; 2 TutorialВ этом руководстве показаны все основы — как создавать интерфейсы с именами и выводами для функционирования шаблонов и шаблонов классов, а также несколько более продвинутых идиом. 2.1 Parameter-Enabled FunctionsВ этом разделе мы покажем, как библиотека Параметров может быть использована для создания экспрессивного интерфейса к библиотекеBoost Graph'sdeep_first_searchалгоритм.1 2.1.1 Headers And NamespacesБольшинство компонентов библиотеки параметров объявлены в заголовке, названном по имени компонента. Например, #include <boost/parameter/keyword.hpp> бустер::параметр::ключевое словоизвестно компилятору. Существует также комбинированный заголовокboost/parameter.hpp, который включает в себя большинство компонентов библиотеки. Для остальной части этого учебника, если мы не скажем иначе, вы можете использовать правило выше, чтобы выяснить, какой заголовок#включитьдля доступа к любому заданному компоненту библиотеки. Приведенные ниже примеры также будут написаны как псевдоним пространства имен. namespace parameter = boost::parameter; Заявлено: напишемпараметр::xxxвместобустер::parameter:::xxx. 2.1.2 The Abstract Interface to depth_first_searchАлгоритм библиотеки графовdeep_first_searchявляется общей функцией, принимающей от одного до четырех аргументов посредством ссылки. Если потребуются все аргументы, то его подпись может быть следующей: 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); Однако большинство параметров имеют полезное значение по умолчанию, как показано в таблице ниже.
Не пугайтесь информации во второй и третьей колонках выше. Для целей этого упражнения вам не нужно понимать их подробно. 2.1.3 Defining the KeywordsСмысл этого упражнения состоит в том, чтобы дать возможность вызватьdeep_first_searchс именованными аргументами, исключив любые аргументы, для которых подходит по умолчанию: graphs::depth_first_search(g, color_map_=my_color_map); Чтобы сделать этот синтаксис законным, должен быть объект под названием «color_map_», оператор назначения которого может принять аргументmy_color_map. На этом этапе мы создадим один такойобъект ключевых словдля каждого параметра. Каждый объект ключевого слова будет идентифицирован уникальнымтипом тега ключевого слова. Мы собираемся определить наш интерфейс в пространстве именграфов. Библиотека предоставляет удобный макрос для определения объектов ключевых слов: #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) } Объявление графаключевого слова, которое вы видите здесь, эквивалентно: namespace graphs { namespace tag { struct graph; } // keyword tag type namespace // unnamed { // A reference to the keyword object boost::parameter::keyword<tag::graph>& _graph = boost::parameter::keyword<tag::graph>::get(); } } Он определяеттип тега ключевого слова, названныйтег::graphиобъект ключевого словассылка, названная_graph. Этот «причудливый танец» с участием неназванного пространства имен и ссылок делается для того, чтобы избежать нарушения Правила Одного Определения (ODR)2, когда названный интерфейс параметров используется шаблонами функций, которые инстанцируются в нескольких единицах перевода (пользователи MSVC6.x видятэту заметку). 2.1.4 Writing the FunctionТеперь, когда мы определили наши ключевые слова, определение шаблона функции следует простому шаблону с использованием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 (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 } } Аргументы в пользуBOOST_PARAMETER_FUNCTION:
2.1.5 Function SignaturesФункциональные сигнатуры описываются как один или два соседних скобчатых термина (aBoost.Preprocessorпоследовательность), описывающих параметры функции в том порядке, в котором они должны были бы ожидаться, если бы проходили позиционно. Любые требуемые параметры должны быть первыми, но пункт 133 (обязательно...)может быть опущен, когда все параметры являются необязательными. 2.1.5.1 Required ParametersRequired 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 (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. 2.1.5.2 Optional ParametersOptional 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 (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” ParametersWithin 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(…): (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) ) ) Если быcolor_mapбыли строго изменены, но не исследованы, мы могли бы написатьout(color_map). Нет никакой функциональной разницы междуoutиin_out; библиотека предоставляет оба, чтобы вы могли сделать свои интерфейсы более самодокументирующимися. 2.1.5.4 Positional ArgumentsКогда аргументы передаются позиционно (без использования ключевых слов), они отображаются на параметры в порядке, в котором параметры указаны в подписи, например, в этом вызове. graphs::depth_first_search(x, y); xвсегда будет интерпретироваться как график, аyвсегда будет интерпретироваться как посетитель. 2.1.5.5 Default Expression EvaluationNote that in our example, the value of the graph parameter is used in the default expressions for root_vertex, index_map and color_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: #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. 2.1.5.6 Signature Matching and OverloadingФактически, подпись функции настолько общая, что любой вызовdeep_first_searchс менее чем пятью аргументами будет соответствовать нашей функции, при условии, что мы передаемчто-тодля требуемогографапараметра. Поначалу это может показаться проблемой; в конце концов, если аргументы не соответствуют требованиям, предъявляемым реализациейdeep_first_search, ошибка компиляции произойдет позже, когда ее тело будет инстанциировано. Существует как минимум три проблемы с очень общими функциональными подписями.
Обычно это хорошая идея, чтобы предотвратить рассмотрение функций для разрешения перегрузки, когда принятые типы аргументов не подходят. Библиотека уже делает это, когда требуемыйграфпараметр не поставляется, но мы вряд ли увидим глубину первого поиска, который не принимает граф для работы. Предположим, вместо этого, что мы нашли другой алгоритм поиска глубины, который мог бы работать на графиках, которые не моделируются.График заболеваемостиЕсли бы мы просто добавили простую перегрузку, это было бы неоднозначно: // 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Мы действительно не хотим, чтобы компилятор рассматривал первоначальную версиюdeep_first_search, потому что аргументroot_vertex,"hello", не соответствуеттребованию, что он соответствуетграфупараметра. Вместо этого этот призыв должен просто вызвать нашу новую перегрузку. Чтобы взять исходнуюглубину_первый_поискперегрузку из спора, нужно сообщить библиотеке об этом требовании, заменив*элемент подписи на требуемый тип, в скобках: (root_vertex, (typename boost::graph_traits<graph_type>::vertex_descriptor), *vertices(graph).first) Теперь исходныйdeep_first_searchбудет называться только тогда, когда аргументroot_vertexможет быть преобразован в тип дескриптора вершины графа, и наш пример того, чтобылнеоднозначным, плавно назовет новую перегрузку. 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. 2.1.5.6.2 Predicate RequirementsТребования к другим аргументам немного интереснее, чем кroot_vertex; их нельзя описать в терминах простого сопоставления типов. Вместо этого они должны быть описаны в терминахMPL метафункций. Здесь нет места для полного описания метафункций или подробностей графовой библиотеки, но мы покажем вам полную подпись с максимальной проверкой, просто чтобы дать вам представление о том, как это делается. Каждая метафункция предиката заключена в скобкии предшествует астериксследующим образом: // We first need to define a few metafunction that we use in the // predicates below. template <class G> struct traversal_category { typedef typename boost::graph_traits<G>::traversal_category type; }; template <class G> struct vertex_descriptor { typedef typename boost::graph_traits<G>::vertex_descriptor type; }; template <class G> struct value_type { typedef typename boost::property_traits<G>::value_type type; }; template <class G> struct key_type { typedef typename boost::property_traits<G>::key_type type; }; template<class Size, class IndexMap> boost::iterator_property_map< boost::default_color_type*, IndexMap , boost::default_color_type, boost::default_color_type& > default_color_map(Size num_vertices, IndexMap const& index_map) { std::vector<boost::default_color_type> colors(num_vertices); return &colors[0]; } BOOST_PARAMETER_FUNCTION( (void), depth_first_search, graphs , (required (graph , *(boost::mpl::and_< boost::is_convertible< traversal_category<_>, boost::incidence_graph_tag > , boost::is_convertible< traversal_category<_>, boost::vertex_list_graph_tag > >) )) (optional (visitor, *, boost::dfs_visitor<>()) // not checkable (root_vertex , (vertex_descriptor<graphs::graph::_>) , *vertices(graph).first) (index_map , *(boost::mpl::and_< boost::is_integral<value_type<_> > , boost::is_same< vertex_descriptor<graphs::graph::_>, key_type<_> > >) , get(boost::vertex_index,graph)) (in_out(color_map) , *(boost::is_same< vertex_descriptor<graphs::graph::_>, key_type<_> >) , default_color_map(num_vertices(graph), index_map) ) ) ) Обратите внимание на использование вложенноготега::_. Это короткий путь для: value_type<boost::mpl::_2, tag> Предназначен для доступа к предыдущим типам аргументов в предикатах. Мы признаем, что эта подпись довольно волосатая. К счастью, обычно нет необходимости полностью кодировать требования к типу аргументов для общих функций. Тем не менее, это стоит усилий: ваш код будет более самодокументирующим и часто обеспечит лучший пользовательский опыт. У вас также будет более легкий переход на новый стандарт C++ с языковой поддержкойдля концепций. 2.1.5.7 Deduced ParametersЧтобы проиллюстрировать поддержку выведенных параметров, мы должны оставить наш пример из библиотеки Графа. Вместо этого рассмотрим пример функцииdefизBoost.Python. Его подпись примерно следующая: 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() ); Постарайтесь не слишком отвлекаться на использование термина «ключевые слова» в этом примере: хотя он и означает нечто аналогичное в Boost. Python, что он означает в библиотеке параметров, для целей этого упражнения вы можете думать о нем как о совершенно другом. При вызовеdefтребуется только два аргумента. Связь между любыми дополнительными аргументами и их параметрами может быть определена типами фактически принятых аргументов, поэтому абоненту не требуется запоминать позиции аргументов или явно указывать имена параметров для этих аргументов. Чтобы сгенерировать этот интерфейс с помощьюBOOST_PARAMETER_FUNCTION, нам нужно только включить выведенные параметры в(выведено ...)пункт, следующим образом: 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::_>) // see5 , no_keywords()) (policies , *(mpl::not_< mpl::or_< boost::is_convertible<mpl::_, char const*> , is_keyword_expression<mpl::_> // see5 > >) , 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. С приведенным выше заявлением эквивалентны два следующих вызова: def("f", &f, some_policies, "Documentation for f"); def("f", &f, "Documentation for f", some_policies); Если пользователь хочет передать аргументполитики, который также по какой-то причине был конвертируем вchar const*, он всегда может четко указать имя параметра следующим образом: def( "f", &f , _policies = some_policies, "Documentation for f"); 2.2 Parameter-Enabled Member FunctionsBOOST_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), call, tag, (required (arg1,(int))(arg2,(int)))) { std::cout << arg1 << ", " << arg2 << std::endl; } }; Эти макросы не позволяют напрямую отделить интерфейс функции от ее реализации, но вы всегда можете переслать аргументы на отдельную функцию реализации: struct callable2 { BOOST_PARAMETER_CONST_MEMBER_FUNCTION( (void), call, tag, (required (arg1,(int))(arg2,(int)))) { call_impl(arg1,arg2); } private: void call_impl(int, int); // implemented elsewhere. }; 2.2.1 Static Member FunctionsЧтобы открыть функцию статического члена, просто вставьте ключевое слово «статическое» перед именем функции: BOOST_PARAMETER_NAME(arg1) struct somebody { BOOST_PARAMETER_MEMBER_FUNCTION( (void), static f, tag, (optional (arg1,(int),0))) { std::cout << arg1 << std::endl; } }; 2.3 Parameter-Enabled ConstructorsОтсутствие функции «делегирующего конструктора» в C++http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf) несколько ограничивает качество интерфейса, который эта библиотека может обеспечить для определения конструкторов с параметрами. Обычный обходной путь для отсутствия делегирования конструктора применим: необходимо учитывать общую логику в базовом классе. Давайте создадим конструктор с параметрами, который просто печатает свои аргументы. Первый шаг — написать базовый класс, чей конструктор принимает один аргумент, известный как.ArgumentPack: Пакет ссылок на фактические аргументы, помеченные их ключевыми словами. Значения фактических аргументов извлекаются из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; } }; Обратите внимание, что оператор по биту или («|») имеет особое значение при применении к объектам ключевых слов, которые передаются оператору индексацииArgumentPack: он используется для указания значения по умолчанию. В этом случае, если вArgumentPackнетиндексапараметра, вместо этого будет использоваться42. Теперь мы готовы написать интерфейс конструктора с поддержкой параметров: struct myclass : myclass_impl { BOOST_PARAMETER_CONSTRUCTOR( myclass, (myclass_impl), tag , (required (name,*)) (optional (index,*))) // no semicolon }; Поскольку мы предоставили значение по умолчанию дляиндекса, но не дляимени, требуется толькоимя. Мы можем использовать наш новый интерфейс следующим образом: myclass x("bob", 3); // positional myclass y(_index = 12, _name = "sally"); // named myclass z("june"); // positional/defaulted Более подробную информацию о манипулированииArgumentPackсм. в разделеAdvanced Topics. 2.4 Parameter-Enabled Class TemplatesВ этом разделе мы используем Boost. Параметр для построения шаблонаBoost.Python'sclass_, «подпись» которого: template class< ValueType, BaseList = bases<> , HeldType = ValueType, Copyable = void > class class_; Требуется только первый аргументValueType. 2.4.1 Named Template ParametersВо-первых, мы создадим интерфейс, который позволит пользователям передавать аргументы позиционно или по имени: 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Первым шагом является определение ключевых слов для каждого параметра шаблона: 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) }} Объявление ключевого слова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> {}; }} Он определяет тип тега ключевого слова под названиемтег::class_typeи шаблонпрохождения параметровпод названиемclass_type. 2.4.1.2 Class Template SkeletonСледующим шагом является определение скелета шаблона нашего класса, который имеет три дополнительных параметра. Поскольку пользователь может передавать аргументы в любом порядке, мы не знаем фактических идентичностей этих параметров, поэтому было бы преждевременно использовать описательные имена или записывать фактические значения по умолчанию для любого из них. Вместо этого мы дадим им общие имена и используем специальный тип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Далее нужно построить тип, известный как.ParameterSpec, описывающий «подпись»повышения::python::class_. AParameterSpecперечисляет требуемые и необязательные параметры в их позиционном порядке вместе с любыми требованиями типа (обратите внимание, что внеуказаны по умолчанию — они будут рассматриваться отдельно): namespace boost { namespace python { using boost::mpl::_; typedef parameter::parameters< required<tag::class_type, boost::is_class<_> > , parameter::optional<tag::base_list, mpl::is_sequence<_> > , parameter::optional<tag::held_type> , parameter::optional<tag::copyable> > class_signature; }} 2.4.1.4 Argument Packs and Parameter ExtractionДалее, в корпусеclass_, мы используемParameterSpecвложенный::bind< ... >шаблон, чтобы связать фактические аргументы вArgumentPackтип, а затем использоватьзначение библиотеки_type<...>метафункцию для извлечения «логических параметров».value_type<... >во многом похожа на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::value_type< args, tag::class_type>::type class_type; typedef typename parameter::value_type< args, tag::base_list, bases<> >::type base_list; typedef typename parameter::value_type< args, tag::held_type, class_type>::type held_type; typedef typename parameter::value_type< args, tag::copyable, void>::type copyable; }; }} 2.4.2 Exercising the Code So FarRevisiting 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Чтобы применить интерфейс выводимых параметров, нам нужно только сделать требования к типу немного более жесткими, чтобы параметрыheld_typeископируемыеможно было четко отличить от других.Boost.Pythonделает это, требуя, чтобыbase_listбыл специализацией егоbases< ... >шаблон (в отличие от любой старой последовательности MPL) и требуя, чтобыкопируемый, если явно поставляется, былбустер::некопируемый. Один из простых способов определения специализацийоснований< ... >состоит в том, чтобы вывести их все из одного класса в качестве детали реализации: namespace boost { namespace python { namespace detail { struct bases_base {}; } template <class A0 = void, class A1 = void, class A2 = void … > struct bases : detail::bases_base {}; }} Теперь мы можем переписать нашу подпись, чтобы сделать все три дополнительных параметра выводимыми: typedef parameter::parameters< required<tag::class_type, is_class<_> > , parameter::optional< deduced<tag::base_list> , is_base_and_derived<detail::bases_base,_> > , parameter::optional< deduced<tag::held_type> , mpl::not_< mpl::or_< is_base_and_derived<detail::bases_base,_> , is_same<noncopyable,_> > > > , parameter::optional<deduced<tag::copyable>, is_same<noncopyable,_> > > class_signature; Может показаться, что мы добавили много сложностей, но преимущества для наших пользователей больше. Наши оригинальные примеры теперь могут быть написаны без явных имен параметров: typedef boost::python::class_<B, boost::noncopyable> c1; typedef boost::python::class_<D, std::auto_ptr<D>, bases<B> > c2; 3 Advanced TopicsВ этот момент вы должны хорошо понимать основы. В этом разделе мы рассмотрим более эзотерическое использование библиотеки. 3.1 Fine-Grained Name ControlЕсли вам не нравится соглашение о присвоении имен, используемое для обозначения объектов ключевых слов, или вам нужно имятегдля чего-то, кроме пространства имен типа ключевого слова, есть другой способ использования. BOOST_PARAMETER_NAME: BOOST_PARAMETER_NAME((object-name, tag-namespace) parameter-name) Вот пример использования: BOOST_PARAMETER_NAME((pass_foo, keywords) foo) BOOST_PARAMETER_FUNCTION( (int), f, keywords, (required (foo, *))) { return foo + 1; } int x = f(pass_foo = 41); Однако, прежде чем использовать эту более многословную форму, пожалуйста, прочитайте раздел олучших практиках для обозначения объектов ключевых слов. 3.2 More ArgumentPacksМы уже виделиArgumentPacks, когда смотрели наконструкторы с параметрамиишаблоны классов. Как вы уже догадались,ArgumentPackлежит в основе всего, что делает эта библиотека; в этом разделе мы рассмотрим способы создания и более эффективного управления ими. 3.2.1 Building ArgumentPacksСамый простой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" Кроме того,ArgumentPacks может быть составлен с помощью оператора запятой. Дополнительные скобки ниже используются для предотвращения того, чтобы компилятор видел два отдельных аргумента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")); Для построенияArgumentPackс позиционными аргументами можно использоватьParameterSpec. Как описано в разделеПодписи шаблонов классов, aParameterSpecописывает позиционный порядок параметров и любые соответствующие требования типа. Так же, как мы можем построитьArgumentPackтипс его вложенным::bind<... >шаблоном, мы можем построитьArgumentPackобъект, вызывая его оператор вызова функции: 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") ); Обратите внимание, что из-запроблемы пересылки,параметр::параметры::оператор()не может принимать неконстовые значения. 3.2.2 Extracting Parameter TypesЕсли мы хотим знать типы аргументов, переданныхprint_name_and_index, у нас есть несколько вариантов. Самый простой и наименее подверженный ошибкам подход заключается в том, чтобы перенаправить их в шаблон функций и позволитьсделать вывод типа: 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]); } Иногда нужно вывести типы аргументов без дополнительного уровня вызова функции. Например, предположим, что мы хотели вернуть в два раза больше значения параметраиндекса? В этом случае мы можем использоватьзначение_type<...>метафункцию, введеннуюранее: BOOST_PARAMETER_NAME(index) template <class ArgumentPack> typename parameter::value_type<ArgumentPack, tag::index, int>::type twice_index(ArgumentPack const& args) { return 2 * args[_index|42]; } int six = twice_index(_index = 3); Обратите внимание, что если бы мы использовалисвязывание< ... >, а незначение_type< ... >, мы бы в конечном итоге вернули ссылку на временное создание в. 2 *...выражение. 3.2.3 Lazy Default ComputationКогда значение по умолчанию дорого для вычисления, было бы предпочтительнее избегать его, пока мы не уверены, что это абсолютно необходимо.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 return s3; } std::string x = f((_s1="hello,", _s2=" world", _s3="hi world")); В приведенном выше примере строка"hello, world"построена, несмотря на то, что пользователь передал нам значениеs3. Чтобы исправить это, мы можем вычислить значение по умолчаниюлениво(то есть только по требованию), используяимпульс::bind()для создания функционального объекта. typename parameter::binding< ArgumentPack, 4 Best Practices 5 Portability ConsiderationsИспользуйте результаты регрессионного тестадля последней версии библиотеки параметров, чтобы увидеть, как она работает на вашем любимом компиляторе. Кроме того, вам может потребоваться знать о следующих проблемах и обходных путях для конкретных компиляторов. 5.1 No SFINAE SupportНекоторые старые компиляторы не поддерживают SFINAE. Если компилятор соответствует этому критерию, то заголовки Boost будут#defineсимвол препроцессораBOOST_NO_SFINAE, а функции с поддержкой параметров не будут удалены из набора перегрузок на основе их подписей. 5.2 No Support for result_ofЛенивые вычисления по умолчаниюоснованы на шаблоне классаresult_ofдля вычисления типов аргументов по умолчанию с учетом типа объекта функции, который их строит. На компиляторах, которые не поддерживаютresult_of,BOOST_NO_RESULT_OFбудет#defined, и компилятор будет ожидать, что объект функции будет содержать вложенное имя типа,result_type, что указывает его тип возврата при вызове без аргументов. Чтобы использовать обычную функцию в качестве генератора по умолчанию на этих компиляторах, вам нужно обернуть ее в класс, который обеспечиваетresult_typeв качествеtypedefи вызывает функцию через своегооператора(). 5.3 Compiler Can't See References In Unnamed NamespaceЕсли вы используете Microsoft Visual C++ 6.x, у компилятора могут возникнуть проблемы с поиском объектов ключевых слов. Эта проблема была замечена, но только на этом компиляторе, и она исчезла по мере развития тестового кода, поэтому мы предлагаем использовать его только в качестве последнего средства, а не в качестве профилактической меры. Решение состоит в том, чтобы добавитьзаявления об использовании, чтобы заставить имена быть доступными в замкнутом пространстве имен без квалификации: namespace graphs { using graphs::graph; using graphs::visitor; using graphs::root_vertex; using graphs::index_map; using graphs::color_map; } 6 Python BindingСледуйтеэтой ссылкедля документации о том, как подвергать функции с поддержкой Boost.Parameter Python сBoost.Python. 7 Reference,, [скрыто], [скрыто]. Справочная документация по параметрам. 8 Glossary
9 AcknowledgementsАвторы хотели бы поблагодарить всех Бустеров, которые участвовали в обзоре этой библиотеки и ее документации, особенно нашего менеджера по обзору Дуга Грегора.
Статья The Boost Parameter Library раздела может быть полезна для разработчиков на c++ и boost. Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта. :: Главная :: ::
|
|||||||||||||||||||||||||||||||||||||||||||||||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |