📄 index.rst
字号:
+++++++++++++++++++++++++++++++++++++++++++++++++
The Boost Parameter Library
+++++++++++++++++++++++++++++++++++++++++++++++++
|(logo)|__
.. |(logo)| image:: ../../../../boost.png
:alt: Boost
__ ../../../../index.htm
-------------------------------------
:Abstract: Use this library to write functions that accept
arguments by name:
.. parsed-literal::
new_window("alert", **width=10**, **titlebar=false**);
Since named arguments can be passed in any order, they are
especially useful when a function has more than one parameter
with a useful default value.
-------------------------------------
:Authors: David Abrahams, Daniel Wallin
:Contact: dave@boost-consulting.com, dalwan01@student.umu.se
:Organization: `Boost Consulting`_
:Date: $Date: 2005/07/18 20:34:31 $
:Copyright: 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)
.. _`Boost Consulting`: http://www.boost-consulting.com
.. _concepts: ../../../more/generic_programming.html#concept
-------------------------------------
.. contents:: **Table of Contents**
.. role:: concept
:class: concept
.. role:: vellipsis
:class: vellipsis
.. section-numbering::
-------------------------------------
==============
Introduction
==============
In C++, arguments are normally given meaning by their positions
with respect to a parameter list. 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:
* Since an argument's meaning is given by its position, we have to
choose an (often arbitrary) order for parameters with default
values, making some combinations of defaults unusable:
.. parsed-literal::
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);
In the example above we wanted to make an unmoveable window
with a default ``border_width``, but instead we got a moveable
window with a ``border_width`` of zero. To get the desired
effect, we'd need to write:
.. parsed-literal::
window* w = new_window(
"alert box", **default_border_width**, movability);
* It can become difficult for readers to understand the meaning of
arguments at the call site::
window* w = new_window("alert", 1, true, false);
Is this window moveable and initially invisible, or unmoveable
and initially visible? The reader needs to remember the order
of arguments to be sure.
* The author of the call may not remember the order of the
arguments either, leading to hard-to-find bugs.
This library addresses the problems outlined above by associating
each parameter with a keyword object. Now users can identify
arguments by keyword, rather than by position:
.. parsed-literal::
window* w = new_window("alert box", **movable=**\ false); // OK!
.. I'm inclined to leave this part out. In particular, the 2nd
point is kinda lame because even with the library, we need to
introduce overloads -- dwa:
C++ has two other limitations, with respect to default arguments,
that are unrelated to its positional interface:
* Default values cannot depend on the values of other function
parameters:
.. parsed-literal::
// Can we make resize windows to a square shape by default?
void resize(
window* w,
int **width**,
int height **= width** // nope, error!
);
* Default values in function templates are useless for any
argument whose type should be deduced when the argument is
supplied explicitly::
template <class T>
void f(T x = 0);
f(3.14) // ok: x supplied explicitly; T is double
f(); // error: can't deduce T from default argument 0!
As a side effect of using the Boost Parameter library, you may find
that you circumvent both of these limitations quite naturally.
==========
Tutorial
==========
In this section we'll show how the Parameter library can be used to
build an expressive interface to the `Boost Graph library`__\ 's
|dfs|_ algorithm. [#old_interface]_ After laying some groundwork
and describing the algorithm's abstract interface, we'll show you
how to build a basic implementation with keyword support. Then
we'll add support for default arguments and we'll gradually refine the
implementation with syntax improvements. Finally we'll show how to
streamline the implementation of named parameter interfaces,
improve their participation in overload resolution, and optimize
their runtime efficiency.
__ ../../../graph/index.html
.. _dfs: ../../../graph/doc/depth_first_search.html
.. |dfs| replace:: ``depth_first_search``
Headers And Namespaces
======================
Most components of the Parameter library are declared in a
header named for the component. For example, ::
#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.
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``.
The Abstract Interface to |dfs|
===============================
The Graph library's |dfs| algorithm is a generic function accepting
from one to four arguments by reference. If all arguments were
required, its signature might be as follows::
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.
.. _`parameter table`:
.. _`default expressions`:
.. table:: ``depth_first_search`` Parameters
+----------------+----------+----------------------------------+
| Parameter Name | Dataflow | Default Value (if any) |
+================+==========+==================================+
|``graph`` | in |none - this argument is required. |
+----------------+----------+----------------------------------+
|``visitor`` | in |``boost::dfs_visitor<>()`` |
+----------------+----------+----------------------------------+
|``root_vertex`` | in |``*vertices(graph).first`` |
+----------------+----------+----------------------------------+
|``index_map`` | in |``get(boost::vertex_index,graph)``|
+----------------+----------+----------------------------------+
|``color_map`` | out |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. |
+----------------+----------+----------------------------------+
Don't be intimidated by the complex default values. For the
purposes of this exercise, you don't need to understand what they
mean. Also, we'll show you how the default for ``color_map`` is
computed later in the tutorial; trust us when we say that the
complexity of its default will become valuable.
Defining the Keywords
=====================
The point of this exercise is to make it possible to call
``depth_first_search`` with keyword arguments, leaving out any
arguments for which the default is appropriate:
.. parsed-literal::
graphs::depth_first_search(g, **color_map = my_color_map**);
To make that syntax legal, there needs to be an object called
``color_map`` with an assignment operator that 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**.
We're going to define our interface in namespace ``graphs``. Since
users need access to the keyword objects, but not the tag types,
we'll define the keyword objects so they're acceessible through
``graphs``, and we'll hide the tag types away in a tested
namespace, ``graphs::tag``. The library provides a convenient
macro for that purpose (MSVC6.x users see this note__)::
#include <boost/parameter/keyword.hpp>
namespace graphs
{
BOOST_PARAMETER_KEYWORD(tag, graph) // Note: no semicolon
BOOST_PARAMETER_KEYWORD(tag, visitor)
BOOST_PARAMETER_KEYWORD(tag, root_vertex)
BOOST_PARAMETER_KEYWORD(tag, index_map)
BOOST_PARAMETER_KEYWORD(tag, color_map)
}
__ `Compiler Can't See References In Unnamed Namespace`_
The declaration of the ``visitor`` keyword you see here is
equivalent to::
namespace graphs
{
namespace tag { struct visitor; }
namespace {
boost::parameter::keyword<tag::visitor>& visitor
= boost::parameter::keyword<tag::visitor>::get();
}
}
This “fancy dance” involving the unnamed namespace and references
is all done to avoid violating the One Definition Rule (ODR)
[#odr]_ when the named parameter interface is used by function
templates that are instantiated in multiple translation
units.
Defining the Implementation Function
====================================
Next we can write the skeleton of the function that implements
the core of ``depth_first_search``::
namespace graphs { namespace core
{
template <class ArgumentPack>
void depth_first_search(ArgumentPack const& args)
{
// algorithm implementation goes here
}
}}
.. |ArgumentPack| replace:: :concept:`ArgumentPack`
``core::depth_first_search`` has an |ArgumentPack|
parameter: a bundle of references to the arguments that the caller
passes to the algorithm, tagged with their keywords. To extract
each parameter, just pass its keyword object to the
|ArgumentPack|\ 's subscript operator. Just to get a feel for how
things work, let's add some temporary code to print the arguments:
.. parsed-literal::
namespace graphs { namespace core
{
template <class ArgumentPack>
void depth_first_search(ArgumentPack const& args)
{
std::cout << "graph:\\t" << **args[graph]** << std::endl;
std::cout << "visitor:\\t" << **args[visitor]** << std::endl;
std::cout << "root_vertex:\\t" << **args[root_vertex]** << std::endl;
std::cout << "index_map:\\t" << **args[index_map]** << std::endl;
std::cout << "color_map:\\t" << **args[color_map]** << std::endl;
}
}} // graphs::core
It's unlikely that many of the arguments the caller will eventually
pass to ``depth_first_search`` can be printed, but for now the code
above will give us something to experiment with. To see the
keywords in action, we can write a little test driver:
.. parsed-literal::
int main()
{
using namespace graphs;
core::depth_first_search(**(**
graph = 'G', visitor = 2, root_vertex = 3.5,
index_map = "hello, world", color_map = false\ **)**);
}
An overloaded comma operator (``operator,``) combines the results
of assigning to each keyword object into a single |ArgumentPack|
object that gets passed on to ``core::depth_first_search``. The
extra set of parentheses you see in the example above are required:
without them, each assignment would be interpreted as a separate
function argument and the comma operator wouldn't take effect.
We'll show you how to get rid of the extra parentheses later in
this tutorial.
Of course, we can pass the arguments in any order::
int main()
{
using namespace graphs;
core::depth_first_search((
root_vertex = 3.5, graph = 'G', color_map = false,
index_map = "hello, world", visitor = 2));
}
either of the two programs above will print::
graph: G
visitor: 2
root_vertex: 3.5
index_map: hello, world
color_map: false
Adding Defaults
===============
Currently, all the arguments to ``depth_first_search`` are
required. If any parameter can't be found, there will be a
compilation error where we try to extract it from the
|ArgumentPack| using the subscript operator. To make it
legal to omit an argument we need to give it a default value.
Syntax
------
We can make any of the parameters optional by following its keyword
with the ``|`` operator and the parameter's default value within
the square brackets. In the following example, we've given
``root_vertex`` a default of ``42`` and ``color_map`` a default of
``"hello, world"``.
.. parsed-literal::
namespace graphs { namespace core
{
template <class ArgumentPack>
void depth_first_search(ArgumentPack const& args)
{
std::cout << "graph:\\t" << args[graph] << std::endl;
std::cout << "visitor:\\t" << args[visitor] << std::endl;
std::cout << "root_vertex:\\t" << args[root_vertex\ **|42**\ ] << std::endl;
std::cout << "index_map:\\t" << args[index_map] << std::endl;
std::cout << "color_map:\\t" << args[color_map\ **|"hello, world"**\ ] << std::endl;
}
}} // graphs::core
Now we can invoke the function without supplying ``color_map`` or
``root_vertex``::
core::depth_first_search((
graph = 'G', index_map = "index", visitor = 6));
The call above would print::
graph: G
visitor: 6
root_vertex: 42
index_map: index
color_map: hello, world
.. Important::
The index expression ``args[…]`` always yields a *reference*
that is bound either to the actual argument passed by the caller
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -