Boost logo

Boost :

From: Daniel Wallin (dalwan01_at_[hidden])
Date: 2006-02-18 14:42:37


David A. Greene wrote:
> David Abrahams wrote:
>
>>> BOOST_NTP (Elemtype);
>>> BOOST_NTP_C(Bufsize);
>>> BOOST_NTP_C(Stride);
>>> BOOST_NTP_C(Iterations);
>>
>> I don't know what's under the covers of those macros; we've tried to
>> make sure that there's a macro-less interface that's at least
>> reasonably usable, so those would be:
>>
>> template <class T = int>
>> struct Elemtype_is
>> : parameter::template_keyword<a0_is<>, T>
>> {};
>
[...]
> It's very close to what you specified. I just find something less
> wordy easier to comprehend. I don't know what a0_is<> is, for
> example.

a0_is<> was a typo, it's supposed to be:

   template <class T = int>
   struct Elemtype_is
     : parameter::template_keyword<Elemtype_is<>, T>
   {};

The point of the T's default value is that we can use Elemtype_is<> as a
tag-type for the parameter, instead of having a separate tag.

[...]
> If I understand your interface correctly, "params" holds both the
> formal arguments that participate in NTP binding and the NTP names.
> Default specification occurs at lookup time.
>
> We've just separated steps a little bit differently. Right now I
> don't have a strong preference for either, though I would like
> to understand the purpose of parameters<>::bind<> better. What
> kind of binding is being done?

parameters<>::bind<> returns an ArgumentPack with keyword tags bound to
actual argument types. This ArgumentPack can then be queried for keyword
tags with binding<>.

>> typedef typename parameter::binding<
>> params, Elemtype_is<>, int
>> >::type ElemtypeP;
>>
>>
>> typedef typename parameter::binding<
>> params, Stride_is<>, mpl::int_<1>
>> >::type StrideP;
>
> As I understand it, this both specifies defaults and does the
> actual mapping between actual arguments and NTP names.

Yes. It's actually quite important to specify the default as late as
possible, because defaults can depend on the values of other arguments.

>>> My goals with this interface are:
>>>
>>> - Allow arbitrary reordering of parameters
>>>
>>> - Unspecified parameters use defaults automatically, regardless
>>> of position of other parameters
>>
>> I don't know what you mean by "automatically." I understand the second
>> part of the sentence.
>
> Poor choice of words on my part. This is really two different goals:
>
> - Show which NTPs have defaults right in the template interface so
> it's clear to the user without having to look at the use of the
> binding mechanism.

This sounds like a good job for documentation. As I mentioned above,
specifying defaults this early means you can't have default dependencies.

>> Doesn't look too easy for the developer. And IMO it would be better
>> for the developer -- who is likely to want to use the parameter
>> library anyway for function parameters -- to reuse his knowledge.
>
> Agreed about reusing knowledge, but what do you find not easy,
> or less easy? I find some of the named_parameters syntax rather
> unwieldy.

What part of the syntax specifically?

>> The parameter library now supplies that. Additionally it has support
>> for "unnamed" parameters (those whose identity can be deduced from the
>> type passed) and the specification of MPL lambda expressions to
>> identify the legal range of types for each parameter (with an error
>> generated if required parameters can't be found).
>
> Yes, unnamed parameters are a missing piece of my work.b Can you
> give an example of the lambda expressions to specify legal types?
> That sounds very useful.

An example could be something that uses policies. Here we can use a
lambda expression to automatically bind the arguments:

  ...

  template <
     class Policy0 = parameter::void_, class Policy1 = parameter::void_
>
  struct thing
  {
      typedef parameter::parameters<
          unnamed<
              policy0_is<>
            , is_base_and_derived<policy0_tag, mpl::_>
>
        , unnamed<
              policy1_is<>
            , is_base_and_derived<policy1_tag, mpl::_>
>
> parameters;

      typedef typename parameters::bind<Policy0,Policy1>::type args;
  };

  struct my_policy : policy1_tag {};

  thing<my_policy> // policy1_is<> is bound to my_policy

>> Ours works differently IIRC; positional parameters bind strictly by
>> position. But I don't see this as being very important, though. Most
>> languages that allow this sort of thing natively require that all
>> positional parameters precede the named ones anyway and I don't see
>> why a user would want to do otherwise.
>
> So how does something like this work:
>
> template<typename A = parameters::unspecified,
> typename B = parameters::unspecified,
> typename C = parameters::unspecified>
> class Foo {
> ...
> };
>
> typedef Foo<int, long, A_is<double> > myfoo;
>
> Is this just not allowed?

No, it's an error. We bind strictly by absolute position. A will be
bound to two types, int and double.

> I would expect B to be bound to int and to long.

Well, that would complicate the implementation quite a lot. And probably
decrease efficiency. We recommend that users use "positional first".

>>> I still don't completely understand unnamed arguments. Does
>>> my last example above cover what you mean?
>> No, please see
>> http://www.boost.org/libs/python/doc/v2/class.html#class_-spec for an
>> example. This predates the parameter library; if I can find the time
>> I'm going to go through Boost.Python and parameter-ize everything.
>
> Ok, I think I get it now. I see how it would be quite useful.
>
> I'll see if I can get some time this weekend to download
> the parameters library with NTPs and see about modifying
> my Perl script to generate tests. No promises I'll have
> anything by Monday, though! ;)

Great!

-- 
Daniel Wallin

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk