diff --git a/doc/named_params.html b/doc/named_params.html new file mode 100755 index 0000000..b4039dd --- /dev/null +++ b/doc/named_params.html @@ -0,0 +1,444 @@ + + + +
+ + +Authors: | David Abrahams, Daniel Wallin | +
---|---|
Contact: | dave@boost-consulting.com, dalwan01@student.umu.se | +
organizations: | Boost Consulting, | +
date: | $Date$ | +
copyright: | Copyright David Abrahams, Daniel Wallin 2003. | +
license: | Use, modification and distribution is subject to 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) | +
In C++ function arguments are given meaning by their position in +the parameter list. This protocol is fine when there are few +parameters with default values, but as the number of parameters +grows, so does the inconvenience of passing arguments in the +correct order, especially in the presence of default values:
++++
+- +
It can become difficult for readers to understand the meaning of +arguments at the call site:
++window* w = new_window("alert", true, true, false, 77, 65); ++- +
Since meaning is given by position, we have to choose some +(often arbitrary) order for parameters with default values, +making some combinations of defaults unusable:
++window* new_window( + char const* name, bool border = true + , bool opaque = true, bool movable = false + , int width = 100, int height = 100); + +const bool movability = true; +window* w = new_window("alert2", movability); // error! ++- +
Default values can not depend on the values of other function +parameters:
++window* new_window( + char const* name, bool border, ... + , int width = 100, int heigh = width); // error! ++- +
Template types can not be deduced from the default values, so +we have to resort to overloading to provide default values for +parameters with template type:
++template<class T> void f(T x = 0); + +f(); // error! ++
This library is an attempt to address the problems outlined above +by associating each parameter with a keyword identifier. Using +this library, users can identify parameters by name instead of just +argument position:
++window* w = new_window("alert2", movable = movability); // OK! ++ +
This example shows how to wrap a function:
++void foo(char const* name, float value); ++
to give both parameters names and default values.
+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; + +namespace { + boost::keyword<name_t> name; // keyword objects + boost::keyword<value_t> value; +} ++
Placing these keyword objects in an unnamed namespace will prevent +link errors when you declare keywords in header files [Note: +the tag types should generally not be declared in an unnamed +namespace]. We also need to create a keywords list for our +function. These keywords should be declared in the same order as +their corresponding parameters appear in the function's parameter +list:
++struct foo_keywords + : boost::keywords< + name_t + , value_t + > +{}; ++
+template<class Params> +void foo_impl(const Params&); + +void foo() +{ + foo_impl(foo_keywords()); +} + +template<class A0> +void foo(const A0& a0) +{ + foo_impl(foo_keywords(a0)); +} + +template<class A0, class A1> +void foo(const A0& a0, const A1& a1) +{ + foo_impl(foo_keywords(a0, a1)); +} ++
+template<class Params> +void foo_impl(const Params& params) +{ + std::cout << params[name] << " = " << params[value] << "\n"; +} ++
That's it. The user calls the foo() forwarding functions, with +either positional or named parameters. For instance:
++foo("bar", 3.14f); +foo(value = 6.28f, "baz") ++
Should print:
++bar = 3.14 +baz = 6.28 ++
But we still don't have any default values, leaving any of the +parameters out results in a compilation error:
++foo() +foo("bar") +foo(value = 3) ++
All fails.
+Fortunatly, adding default values to parameters is easy:
++template<class Params> +void foo_impl(const Params& params) +{ + std::cout + << params[name | "unnamed"] << " = " + << params[value | 0] << "\n"; +} ++
We are using operator| to denote the default value of a named +parameter.
+Going back a little to the foo() call that didn't compile:
++foo() +foo("bar") +foo(value = 3) ++
Now compiles, and prints:
++unnamed = 0 +bar = 0 +unnamed = 3 ++
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 1. 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:
++#include <boost/ref.hpp> + +float x; +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<> generated by +boost::ref will be unwrapped automatically by the library.
+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 2, 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.
+This sort of 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. The technique is formalized +in the enable_if utility.
+The named parameters library provides built-in SFINAE support +through the following class template:
++template< + 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.
+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:
++struct foo_keywords + : 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
++template<class A0> +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() +) +{ + foo_impl(x(a0, a1)); +} ++
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*.
+If computing an argument's default value is expensive, it's best +avoided when the argument is supplied by the user. In that case, +the default value can be lazily evaluated using the following +syntax:
++params[keyword || nullary_function]; ++
nullary_function must be a function object that is callable +without arguments, and that indicates its return type via a nested +result_type. Boost.Bind can be used to produce an appropriate +function object from a regular function pointer:
++// expensive default computation function +float default_span(float x, float theta); + +// implementation of bar() +template <class Params> +void bar_impl(Params const& params) +{ + // Extract arguments + float x_ = params[x]; + float theta_ = params[theta | pi]; + float span = params[span || boost::bind(default_span, x_, theta_)]; + ... +} ++
To reduce the work needed to write functions with named parameters, +we supply a macro that generates the boilerplate code.
+Synopsis:
++BOOST_NAMED_PARAMS_FUN( + return_type, function_name, keywords_type + , min_arity, max_arity +); ++
To generate all the forwarding functions and the implementation +function for our example, we need only apply +BOOST_NAMED_PARAMS_FUN this way:
++BOOST_NAMED_PARAMS_FUN(void, foo, foo_keywords, 0, 2) +{ + std::cout + << params[name | "unnamed"] << " = " + << params[value | 0] << "\n"; +} ++
Boost.NamedParams has been confirmed to work on the following compilers:
++++
+- Microsoft VC6 sp5, VC7 3
+- Microsoft VC7.1
+- GCC3.3.1 (cygwin), GCC2.95.3 (cygwin), GCC3.2 (mingw)
+- Metrowerks Codewarrior Pro8 and Pro9 (Windows)
+- Intel C++ 5.0,6.0,7.1,8.0 (Windows)
+- Comeau 4.3.3
+
[1] | 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. + |
[2] | 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; ++ |
[3] | Restrictions doesn't work on these compilers because +of lack of SFINAE support. |