Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

The Boost Parameter Library

Boost , ,

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

The Boost Parameter Library

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.


Authors:David Abrahams, Daniel Wallin
Contact:reference documentation


1   Motivation

В C++,аргументыобычно имеют значение по их позициям относительно спискапараметров: первый аргумент передал карты на первый параметр в определении функции и так далее. Этот протокол хорош, когда есть хотя бы один параметр со значением по умолчанию, но когда есть даже несколько полезных по умолчанию, позиционный интерфейс становится обременительным:

  • Поскольку значение аргумента определяется его позицией, мы должны выбрать (часто произвольный) порядок для параметров со значениями по умолчанию, что делает некоторые комбинации по умолчанию непригодными для использования:

    window* new_window(
       char const* name,
      int border_width = default_border_width,
       bool movable = true,
       bool initially_visible = true
       );
    const bool movability = false;
    window* w = new_window("alert box", movability);
    

    В приведенном выше примере мы хотели сделать неподвижное окно с по умолчаниюшириной границы, но вместо этого мы получили подвижное окно сшириной границынуля. Чтобы получить желаемый эффект, нужно написать:

    window* w = new_window(
       "alert box",default_border_width, movability);
    
  • Читателям может стать трудно понять значение аргументов на сайте вызова:

    window* w = new_window("alert", 1, true, false);
    

    Является ли это окно подвижным и изначально невидимым, или неподвижным и изначально видимым? Читатель должен помнить порядок аргументов, чтобы быть уверенным.

  • Автор призыва также может не помнить порядок аргументов, что приводит к труднодоступным ошибкам.

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:

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:

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);

Однако большинство параметров имеют полезное значение по умолчанию, как показано в таблице ниже.

depth_first_search Parameters
Parameter Name Dataflow Type Default Value (if any)
Граф в МодельГрафик заболеваемостииВертексный список Нет, этот аргумент не требуется.
Посетитель в МодельDFS Посетитель boost::dfs_visitor<>()
root_vertex в ГрафВертикальный дескриптор типа. * вершины (граф.первый)
index_map в МодельКарта читабельных свойствс ключевым типом :=графВертикальный дескриптор и тип значения целого типа. get (boost::vertex_index,graph)
Color_map выходить МодельПрочитайте/напишите карту свойствс ключевым типом :=граф. iterator_property_map, созданный изstd::vectordefault_color_typeразмераnum_vertices(graph)и использующийindex_mapдля карты индекса.

Не пугайтесь информации во второй и третьей колонках выше. Для целей этого упражнения вам не нужно понимать их подробно.

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:

  1. Тип возврата результирующего шаблона функции. Парентезы вокруг типа возврата предотвращают любые запятые, которые могут содержаться в препроцессоре, и всегда требуются.
  2. Название полученного шаблона функций.
  3. Имя пространства имен, где мы можем найти типы тегов, имена которых соответствуют именам параметров функции.
  4. Функциональная подпись.

2.1.5   Function Signatures

Функциональные сигнатуры описываются как один или два соседних скобчатых термина (aBoost.Preprocessorпоследовательность), описывающих параметры функции в том порядке, в котором они должны были бы ожидаться, если бы проходили позиционно. Любые требуемые параметры должны быть первыми, но пункт 133 (обязательно...)может быть опущен, когда все параметры являются необязательными.

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 (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 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     (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

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(…):

(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 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.

(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, ошибка компиляции произойдет позже, когда ее тело будет инстанциировано.

Существует как минимум три проблемы с очень общими функциональными подписями.

  1. К моменту начала нашегоглубокого_первого_поискаон был выбран в качестве лучшей соответствующей перегрузки. Некоторые другиеперегрузкимогли бы сработать, если бы они были выбраны вместо этого. К тому времени, когда мы увидим ошибку компиляции, нет никаких шансов изменить это решение.
  2. Даже если нет перегрузок, сообщения об ошибках, генерируемые во время инстанциации, обычно подвергают пользователей запутанным деталям реализации. Например, пользователи могут видеть ссылки на имена, генерируемыеBOOST_PARAMETER_FUNCTION, такие какграфы::деталь::depth_first_search_with_named_params(или хуже — подумайте о типах ошибок, которые вы получаете от реализации STL, когда делаете ошибку).4
  3. Проблемы с раскрытием таких разрешительных подписей шаблонов функций были предметом большого обсуждения, особенно в присутствиинеквалифицированных вызовов. Если все, что мы хотим, это избежать непреднамеренного поиска, зависящего от аргументов (ADL), мы можем изолироватьdeep_first_searchв пространстве имен, не содержащем типов6, но предположим, что мыхотим, чтобыон был найден через ADL.

Обычно это хорошая идея, чтобы предотвратить рассмотрение функций для разрешения перегрузки, когда принятые типы аргументов не подходят. Библиотека уже делает это, когда требуемыйграфпараметр не поставляется, но мы вряд ли увидим глубину первого поиска, который не принимает граф для работы. Предположим, вместо этого, что мы нашли другой алгоритм поиска глубины, который мог бы работать на графиках, которые не моделируются.График заболеваемостиЕсли бы мы просто добавили простую перегрузку, это было бы неоднозначно:

// 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 Functions

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), 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 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

Чтобы применить интерфейс выводимых параметров, нам нужно только сделать требования к типу немного более жесткими, чтобы параметры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

Теперь вы должны иметь достаточно хорошее представление о том, как использовать библиотеку параметров. В этом разделе приведены некоторые дополнительные вопросы, которые помогут вам более эффективно использовать библиотеку.

4.1   Keyword Naming

BOOST_PARAMETER_NAMEпридает основное значение именам всех объектов ключевых слов, чтобы избежать следующей обычно тихой ошибки:

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!
  }
}

Хотя в приведенном выше случае пользователь пытался передать значение3в качестве параметравозраставg, вместо этого произошло то, чтоfаргументвозрастаполучил переназначенное значение 3, а затем был передан в качестве позиционного аргумента вg. Посколькугпервым позиционным параметром являетсяимя, используется значение по умолчанию длявозрастаи г отпечатков3:42. Наши ведущие подчеркивают условность именования, которая делает эту проблему менее вероятной.

В этом конкретном случае проблема могла бы быть обнаружена, если бы параметр fвозрастабыл сделанconst, что всегда является хорошей идеей, когда это возможно. Наконец, мы рекомендуем использовать закрытое пространство имен для всего кода, но особенно для имен с ведущими подчеркиваниями. Если мы оставим в стороне вышеупомянутое пространство именлюдей, имена в глобальном пространстве имен, начиная с ведущих подчеркиваний, которые зарезервированы для вашего компилятора C++, могут стать безвозвратно двусмысленными с именами в нашем неназванном пространстве имен.

4.2   Namespaces

В наших примерах мы всегда объявляли объекты ключевых слов (неназванное пространство имен) в том же пространстве имен, что и Boost. Функции с поддержкой параметров с использованием этих ключевых слов:

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;
  }
}

У пользователей этих функций есть несколько вариантов:

  1. Полная квалификация:
int x = lib::f(lib::_name = "jill", lib::_index = 1);

Такой подход более многословен, чем хотелось бы многим пользователям.

  1. Сделайте объекты ключевых слов доступными череззаявления об использовании:
using lib::_name;
using lib::_index;
int x = lib::f(_name = "jill", _index = 1);

Эта версия намного лучше на самом сайте вызова, но сами декларации (419) (420) могут быть многословными и трудно управляемыми.

  1. Приведите все пространство имен с помощьюдирективы:
using namespace lib;
int x = f(_name = "jill", _index = 3);

Этот вариант удобен, но он без разбора делает всесодержимоеlibдоступным без квалификации.

Если мы добавим дополнительное пространство имен вокруг объявлений ключевых слов, мы сможем дать пользователям больше контроля:

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;
  }
}

Теперь пользователям нужна только однадиректива, чтобы ввести только имена всех ключевых слов, связанных сlib:

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

4.3   Documentation

Идиомы интерфейса, поддерживаемые Boost. Параметры являются совершенно новыми (для 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

Используйте результаты регрессионного тестадля последней версии библиотеки параметров, чтобы увидеть, как она работает на вашем любимом компиляторе. Кроме того, вам может потребоваться знать о следующих проблемах и обходных путях для конкретных компиляторов.

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

Argument (or “actual argument”):
 the value actually passed to a function or class template
Parameter (or “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:

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

9   Acknowledgements

Авторы хотели бы поблагодарить всех Бустеров, которые участвовали в обзоре этой библиотеки и ее документации, особенно нашего менеджера по обзору Дуга Грегора.


[1]Начиная с версии 1.33.0, библиотека Graph по-прежнему использовалаболее старый механизм параметров, но есть планы изменить его, чтобы использовать Boost. Параметр (эта библиотека) в предстоящем выпуске, сохраняя при этом старый интерфейс доступным для обратной совместимости.
[2]Правило 463 гласит, что любой объект в программе C++ должен иметь одинаковое определение во всех единицах перевода (объектных файлах), которые составляют программу.
[3]Если вы не знакомы с библиотекой Boost Graph, не беспокойтесь о значении каких-либо конкретных деталей, с которыми вы сталкиваетесь. В этом случае вы можете заменить все упоминания типов дескрипторов вершины наintв тексте, и ваше понимание библиотеки параметров не пострадает.
[4]Это основная мотивацияConceptC++.
[5]1,2Здесь мы предполагаем, что существует метафункция предиката— это_keyword_expression, которая может использоваться для идентификации моделей Boost. Ключевое слово Python Понятие выражения.
[6]

Вы всегда можете создать иллюзию того, что функция живет во внешнем пространстве имен, применив декларацию (479):

<
  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.
>.
[7]Эта возможность зависит от поддержки компилятора SFINAE.SFINAE:SзаменаFнедомоганиеIsNотAnEошибка. Если замена типа во время инстанцирования шаблона функции приводит к недействительному типу, ошибка компиляции не испускается; вместо этого перегрузка удаляется из набора перегрузок. Производя недействительный тип подписи функции в зависимости от результата некоторого состояния, мы можем решить, считается ли перегрузка во время разрешения перегрузки. Техника формализована вenable_ifутилита. Самые последние компиляторы поддерживают SFINAE; на компиляторах, которые не поддерживают его, библиотека конфигураций Boost#определяетсимвол. BOOST_NO_SFINAE. См.http://www.semantics.org/once_weakly/w02_SFINAE.pdfдля получения дополнительной информации о SFINAE.

Статья The Boost Parameter Library раздела может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 19:50:05/0.040195941925049/1