Doc tweaks

[SVN r1873]
This commit is contained in:
Dave Abrahams
2004-01-14 14:45:08 +00:00
parent 7c51ce7ae5
commit 9fbc78d669

View File

@@ -105,6 +105,8 @@ First we define the named parameter keywords. This is done by creating
"tag" types for each keyword, and declaring ``keyword<``\ *tag*\
``>`` objects::
#include <boost/named_params.hpp>
struct name_t; // tag types
struct value_t;
@@ -209,107 +211,141 @@ Now compiles, and prints::
bar = 0
unnamed = 3
``reference_wrapper<>``
=======================
Limitations of the Approach
===========================
Our forwarding functions need to take their parameters by const
reference. This is because we need to be able to pass the temporaries
created from the operator= call. Because of this, passing non-const
references that aren't bound to a keyword isn't possible without some help.
Because the keywords' ``operator=`` returns a temporary, and
temporaries cannot be bound to non-``const`` reference parameters,
our forwarding functions need to take their arguments by ``const``
reference [#forwarding]_. As a result, an argument which is bound
to a keyword with ``operator=`` can be transparently passed by
non-const reference, but positional arguments are always passed by
``const`` reference unless we use the `Boost.Ref`_ library to
indicate otherwise::
.. DWA What is "something something ?" supposed to mean?
.. It's was suppose to mean "more?"..
::
#include <boost/ref.hpp>
float x;
foo(value = x); // held type is float&
foo(x); // held type is float const&, need help!
foo(ref(x)); // held type is float&
foo(value = x); // held type is float&
foo(x); // held type is float const&, need help!
foo(boost::ref(x)); // held type is float&
Instances of boost::reference_wrapper<> will un unwrapped to it's held
reference type.
.. _`Boost.Ref`: ../../bind/ref.hpp
SFINAE restrictions
===================
Sometimes it is necessary to restrict the types on which the forwarding
functions can be instantiated. This can be accomplished in C++ by using
SFINAE [#sfinae]_. If type substitution
during the instantiation of a function template results in an invalid
type, no compilation error is emitted; instead the overload is removed
from the overload set. By producing an invalid type in the function
signature depending on the result of some condition, whether or not an
overload is considered during overload resolution can be controlled.
This technique is formalized in the ``enable_if`` pattern [#enable_if]_.
Instances of ``boost::reference_wrapper<>`` generated by
``boost::ref`` will be unwrapped automatically by the library.
.. [#sfinae] Substitution Failure Is Not An Error. Some discussion
of SFINAE goes here.
Controlling Overload Resolution
===============================
.. [#enable_if] Some discussion of ``enable_if`` goes here.
The parameters of our templated forwarding functions are completely
general; in fact, they're a perfect match for any argument type
whatsoever. The problems with exposing such general function
templates have been the subject of much discussion; especially in
the presence of `unqualified calls`__. Probably the safest thing
to do is to isolate the forwarding functions in a namespace
containing no types [#using]_, but often we'd *like* our functions
to play nicely with argument-dependent lookup and other function
overloads. In that case, it's neccessary to somehow remove the
functions from the overload set when the passed argument types
don't meet their needs.
.. more?
__ http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#225
.. DWA What about tutorial for your macro?
Daniel: Under BOOST_NAMED_PARAMS_FUN(), should it be moved?
or do we need a more verbose tutorial?
This overload control can be accomplished in C++ by taking
advantage of SFINAE_ (Substitution Failure Is Not An Error). If
type substitution during the instantiation of a function template
results in an invalid type, no compilation error is emitted;
instead the overload is removed from the overload set. By producing
an invalid type in the function signature depending on the result
of some condition, whether or not an overload is considered during
overload resolution can be controlled. This technique is
formalized in the |enable_if| utility.
Let's say we want to restrict our ``foo()`` so that the ``name``
parameter must be convertible to ``const char*``.
::
The named parameters library provides built-in SFINAE support
through the following class template::
template<
class Keyword
, class HasDefaultValue
class KeywordTag
, class HasDefaultValue // mpl::true_ or mpl::false_
, class Predicate
>
struct arg;
::
The key parameter, ``Predicate`` shall be a unary MPL lambda
expression or `Metafunction Class`_ that, when applied to the
actual type the argument, indicates whether that argument type
meets the function's requirements for that parameter position.
.. _`Metafunction Class`: ../../mpl/doc/ref/Metafunction_Class.html
.. _SFINAE: http://www.semantics.org/once_weakly/w02_SFINAE.pdf
.. |enable_if| replace:: ``enable_if``
.. _enable_if: ../enable_if.html
For example, let's say we want to restrict our ``foo()`` so that
the ``name`` parameter must be convertible to ``const char*``.
We'll replace our use of the ``name_t`` tag with a specialization
of ``boost::arg``:
.. parsed-literal::
struct foo_keywords
: keywords<
arg<
name_t
, mpl::false_
, is_convertible<mpl::_
, const char*>
>
, value_t
>
: boost::keywords<
**boost::arg<
name_t
, mpl::false\_
, is_convertible<mpl::\_, const char\*>
>**
, value_t
>
{};
::
Now we can add an additional optional argument to each of our
``foo`` overloads
.. parsed-literal::
template<class A0>
void foo(const A0& a0
, foo_keywords::restrict<A0>::type x = foo_keywords())
void foo(
const A0& a0
, **foo_keywords::restrict<A0>::type x = foo_keywords()**
)
{
foo_impl(x(a0));
}
template<class A0, class A1>
void foo(const A0& a0, const A1& a1
, foo_keywords::restrict<A0,A1>::type x = foo_keywords())
void foo(
const A0& a0, const A1& a1
, **foo_keywords::restrict<A0,A1>::type x = foo_keywords()**
)
{
foo_impl(x(a0, a1));
}
``BOOST_NAMED_PARAMS_FUN()``
============================
These additional parameters are not intended to be used directly
by callers; they merely trigger SFINAE by becoming illegal types
when the ``name`` argument is not convertible to ``const char*``.
To reduce the work needed to write functions which has named parameters,
Automatic Overload Generation
=============================
To reduce the work needed to write functions with named parameters,
we supply a macro that generates the boilerplate code.
Synopsis::
BOOST_NAMED_PARAMS(
BOOST_NAMED_PARAMS_FUN(
return_type, function_name, keywords_type
, min_arity, max_arity
);
Applying this to our original example, we get::
To generate all the forwarding functions and the implementation
function for our example, we need only apply
``BOOST_NAMED_PARAMS_FUN`` like this::
BOOST_NAMED_PARAMS_FUN(void, foo, foo_keywords, 0, 2)
{
@@ -318,3 +354,27 @@ Applying this to our original example, we get::
<< parms[value | 0] << "\n";
}
-----------------------------
.. [#forwarding] One could provide overloads for ``const`` and
non-``const`` reference versions of each parameter, but that
would quickly become unmanageable. It's known as "the
forwarding problem" and has been described in detail in this
paper__. The combinatorial explosion is avoided for the
parameter of keywords' ``operator=`` because they take only a
single argument.
__ http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
.. [#using] You can always give the illusion that the function
lives in an outer namespace by applying a *using-declaration*::
namespace foo_overloads
{
// foo declarations here
void foo() { ... }
...
}
using foo_overloads::foo;