
I've been Boost.Parameter a bit by now and I have some usability feedback. There is one thing that currently bugs be a bit, the type defined by parameter::binding. All my uses so far have been of the form: typedef typename boost::remove_reference< boost::remove_const< boost::parameter::binding<ArgPack,tag::something>::type >::type >::type something_t; something_t s = args[something]; Or equivalent thereof. So every use I have I end up removing the ref and const because I'm really interested in the original type, and hence want to make a copy. Now I understand the rational outlined in "Eliminating Copies" <http://www.boost.org/libs/parameter/doc/html/index.html#eliminating-copies>. But I think that isn't enough of a justification for dirtying up the user interface, as above. I'd rather that the binding type where the value type so that one would have: typedef typename boost::parameter::binding<ArgPack,tag::something>::type something_t; something_t s = args[something]; Having it that way one still has the option of more naturally using the reference type as; something_t const & q = args[something]; And possibly also providing the container standard binding::value_type, and binding::const_reference. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

Rene Rivera wrote:
I've been Boost.Parameter a bit by now and I have some usability feedback. There is one thing that currently bugs be a bit, the type defined by parameter::binding. All my uses so far have been of the form:
typedef typename boost::remove_reference< boost::remove_const< boost::parameter::binding<ArgPack,tag::something>::type >::type >::type something_t;
something_t s = args[something];
Or equivalent thereof. So every use I have I end up removing the ref and const because I'm really interested in the original type, and hence want to make a copy. Now I understand the rational outlined in "Eliminating Copies" <http://www.boost.org/libs/parameter/doc/html/index.html#eliminating-copies>. But I think that isn't enough of a justification for dirtying up the user interface, as above. I'd rather that the binding type where the value type so that one would have:
typedef typename boost::parameter::binding<ArgPack,tag::something>::type something_t;
something_t s = args[something];
Having it that way one still has the option of more naturally using the reference type as;
something_t const & q = args[something];
I don't think the rationale for this is completely expressed in the docs. The reason binding<> works like this is because of dangling references to defaults. Consider from your example above: something_t const & q = args[something | 0]; When binding<> returns the default type here you will get a dangling reference here: int const& = args[something | 0] Our design handles this by letting binding<> return a reference type when an argument is bound to the keyword, and otherwise return the default type unchanged. I would consider adding a binding_value<> metafunction though. -- Daniel Wallin

Daniel Wallin wrote:
I don't think the rationale for this is completely expressed in the docs. The reason binding<> works like this is because of dangling references to defaults. Consider from your example above:
something_t const & q = args[something | 0];
When binding<> returns the default type here you will get a dangling reference here:
int const& = args[something | 0]
Our design handles this by letting binding<> return a reference type when an argument is bound to the keyword, and otherwise return the default type unchanged.
OK I understand what you are saying, but I don't see it :-) As far as I understand from the docs the binding<>::type has nothing to do with the default value as above as it happens before one gets to extracting the value. I wasn't talking about changing the types that the operator[] return. And also from the docs, the binding<> function has it's own way of declaring what the default type should be, which could be different than the default value AFAICT. Are you saying that operator[] uses the binding<>::type, instead of using the internal "reference" type. Looking at code again... Ah yes it does, but not for all compilers, in some it returns the reference type. I think I would consider that a bug ;-) But given that, would not what I suggested of having binding<>::value_type, binding<>::const_reference, binding<>::reference, etc. And then either having the operator[] use the binding<>::reference. Or another less incompatible change would be to just make those extra binding types available. And the users could use the value_type in most cases.
I would consider adding a binding_value<> metafunction though.
Adding types to the binding<> function seems easier to me. But then I didn't write the code :-) -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

Rene Rivera <grafik.list@redshift-software.com> writes:
Daniel Wallin wrote:
I don't think the rationale for this is completely expressed in the docs. The reason binding<> works like this is because of dangling references to defaults. Consider from your example above:
something_t const & q = args[something | 0];
When binding<> returns the default type here you will get a dangling reference here:
int const& = args[something | 0]
Our design handles this by letting binding<> return a reference type when an argument is bound to the keyword, and otherwise return the default type unchanged.
OK I understand what you are saying, but I don't see it :-) As far as I understand from the docs the binding<>::type has nothing to do with the default value as above as it happens before one gets to extracting the value. I wasn't talking about changing the types that the operator[] return.
Consider the usage you're proposing when you /do/ want a reference to bind to the actual argument: typename binding<Args,tag::k,int>::type const& x = args[k | 0] If the "k" argument wasn't supplied, your int const& will be a dangling reference. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Rene Rivera <grafik.list@redshift-software.com> writes:
Daniel Wallin wrote:
I don't think the rationale for this is completely expressed in the docs. The reason binding<> works like this is because of dangling references to defaults. Consider from your example above:
something_t const & q = args[something | 0];
When binding<> returns the default type here you will get a dangling reference here:
int const& = args[something | 0]
Our design handles this by letting binding<> return a reference type when an argument is bound to the keyword, and otherwise return the default type unchanged. OK I understand what you are saying, but I don't see it :-) As far as I understand from the docs the binding<>::type has nothing to do with the default value as above as it happens before one gets to extracting the value. I wasn't talking about changing the types that the operator[] return.
Consider the usage you're proposing when you /do/ want a reference to bind to the actual argument:
typename binding<Args,tag::k,int>::type const& x = args[k | 0]
If the "k" argument wasn't supplied, your int const& will be a dangling reference.
OK I see it now :-) But it still doesn't change my POV. Here's my perspective with regards to the various use cases, when using defaults since my initial examples did not use defaults: 1. Most of the time I want, and expect, copies. And many times this is easy because on just writes out some preset type and assign: int i = args[something | 1]; But that use case doesn't, to me, equate to the use case when it's not a fixed type: typedef remove_reference<remove_const<binding<...>::type>::type T; T i = args[something | T(1)]; 2. If I'm going to the trouble of getting the reference instead of the value. I'm also going to go the extra effort to not get a dangling reference as I would have even if I wasn't using boost::parameter: int default_i = 0; int const & i = args[something | default_i]; Which also doesn't equate to the case of a variable type: typedef remove_reference<remove_const<binding<...>::type>::type T; T detault_i(0); T const & i = args[something | default_i]; 3. I can see why having the binding type be a reference would signal some forms of dangling refs: typedef binding<...>::type T; T i = args[something | T()]; // error But that seems like a mistake one would make because one is expecting the binding type to be the value_type. 4. And last, I can't remove_reference< remove_const<X> > on some types. In particular last week I was trying to pass in a member function pointer. Which the remove_* functions don't work on, or at least they don't after boost::parameter adds the "const &". So I ended up having to use a boost::function instead. Basically it seems like I end up having to jump through hoops in many use cases just to gain that compiler error on 1 use case and the default value_type in 1 other use case. Maybe I'm just weird and I would see things differently if I used things like the macros and forwarding functions. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

Rene Rivera <grafik.list@redshift-software.com> writes:
David Abrahams wrote:
Rene Rivera <grafik.list@redshift-software.com> writes:
Consider the usage you're proposing when you /do/ want a reference to bind to the actual argument:
typename binding<Args,tag::k,int>::type const& x = args[k | 0]
If the "k" argument wasn't supplied, your int const& will be a dangling reference.
OK I see it now :-) But it still doesn't change my POV. Here's my perspective with regards to the various use cases, when using defaults since my initial examples did not use defaults:
1. Most of the time I want, and expect, copies.
Okay. I'm not sure why, but that may not matter. What matters is that it's _your_ desire and expectation. Do we have any reason to think others will feel the same way? I have no problem with adding a value_binding<> template that does what you want, but so far I don't see why we should change binding<> itself.
And many times this is easy because on just writes out some preset type and assign:
int i = args[something | 1];
But that use case doesn't, to me, equate to the use case when it's not a fixed type:
typedef remove_reference<remove_const<binding<...>::type>::type T; T i = args[something | T(1)];
What do you mean by "equate?" Are you just saying, "it isn't so easy in that case?" You know, you can always write this once: template <class A, class K, class D = void> struct my_binding : remove_const< typename remove_reference< typename binding<A,K,D>::type >::type > {};
2. If I'm going to the trouble of getting the reference instead of the value. I'm also going to go the extra effort to not get a dangling reference as I would have even if I wasn't using boost::parameter:
int default_i = 0; int const & i = args[something | default_i];
That doesn't seem like a good enough reason to make binding<> that much more unsafe.
Which also doesn't equate to the case of a variable type:
typedef remove_reference<remove_const<binding<...>::type>::type T; T detault_i(0); T const & i = args[something | default_i];
3. I can see why having the binding type be a reference would signal some forms of dangling refs:
typedef binding<...>::type T; T i = args[something | T()]; // error
Huh? What error? And what is signalled?
But that seems like a mistake one would make because one is expecting the binding type to be the value_type.
I'm lost
4. And last, I can't remove_reference< remove_const<X> > on some types. In particular last week I was trying to pass in a member function pointer. Which the remove_* functions don't work on, or at least they don't after boost::parameter adds the "const &".
That should be fixed, then! But are you sure your problem isn't that you have the remove_ functions inside-out? remove_reference<remove_const<binding<...>::type>::type That will only remove reference-ness, but not constness, from a reference-to-const. The following compiles just fine for me, FWIW (g++-3.4.4): #include <boost/mpl/assert.hpp> #include <boost/type_traits/is_same.hpp> #include <boost/type_traits/remove_const.hpp> #include <boost/type_traits/remove_reference.hpp> struct X { int g(); }; template <class T> int f(T const& x) { using namespace boost; typedef T const& tcr; BOOST_MPL_ASSERT(( is_same< typename remove_const< typename remove_reference< tcr >::type >::type , T>)); return 0; } int x = f(&X::g); But if I invert the const and reference, of course it fails.
So I ended up having to use a boost::function instead.
??
Basically it seems like I end up having to jump through hoops in many use cases just to gain that compiler error on 1 use case and the default value_type in 1 other use case. Maybe I'm just weird and I would see things differently if I used things like the macros and forwarding functions.
Sorry, I'm totally lost. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (3)
-
Daniel Wallin
-
David Abrahams
-
Rene Rivera