Hello,
I have written an adaptor for functions and function objects which
allows to invoke operations on the pointees when working on a collection
of pointers. For example, it allows to sort a list of pointers by a
predicate applying to the pointees.
To make it work with normal functions and functors, I utilized
boost::unary_traits and boost::binary_traits.
This is the class template for the unary version:
template< typename Operation >
class indirecter_unary
: public std::unary_function< typename
boost::unary_traits<Operation>::argument_type,
typename boost::unary_traits<Operation>::result_type >
{
typedef typename boost::unary_traits<Operation>::argument_type arg_type;
typedef typename boost::unary_traits<Operation>::param_type
param_type;
typedef typename boost::unary_traits<Operation>::result_type
result_type;
typedef typename boost::unary_traits<Operation>::function_type
function_type;
function_type op_;
public:
explicit indirecter_unary( param_type op ): op_(op) {}
result_type operator() (arg_type *arg) const {
return op_(*arg);
}
};
Now, the problem arises when I pass a function which takes references.
Like this:
bool pred( int& a )
{
//...
}
indirecter_unary< bool ()(int&) > fctor(pred);
ext::indirecter_unary
Matthias Kaeppler wrote:
Hello,
I have written an adaptor for functions and function objects which allows to invoke operations on the pointees when working on a collection of pointers. For example, it allows to sort a list of pointers by a predicate applying to the pointees. To make it work with normal functions and functors, I utilized boost::unary_traits and boost::binary_traits.
This is the class template for the unary version:
template< typename Operation > class indirecter_unary : public std::unary_function< typename boost::unary_traits<Operation>::argument_type,
Is this correct? You are advertising that your function has an argument_type that is the same as Operation::argument_type. Don't you want your argument_type to be a pointer? In any event, since you need to redefine argument_type and result_type yourself, there is no point inheriting from std::unary_function.
typename boost::unary_traits<Operation>::result_type > { typedef typename boost::unary_traits<Operation>::argument_type arg_type;
I think, the argument_type you are looking for is
typedef typename boost::remove_reference
typedef typename boost::unary_traits<Operation>::param_type param_type;
IIUC, param_type is supposed to be a type used to pass this function object as a parameter. ie, it should be something like indirecter_unary const&, NOT the param_type of Operation.
typedef typename boost::unary_traits<Operation>::result_type result_type; typedef typename boost::unary_traits<Operation>::function_type function_type; function_type op_; public: explicit indirecter_unary( param_type op ): op_(op) {} result_type operator() (arg_type *arg) const {
^^^^^^^^^^ should be: result_type operator()(argument_type arg) const {
return op_(*arg); } };
Now, the problem arises when I pass a function which takes references. Like this:
bool pred( int& a ) { //... }
indirecter_unary< bool ()(int&) > fctor(pred);
ext::indirecter_unary
': code/FileBrowser.cpp:90: instantiated from here code/ext/indirect.hpp:80: error: forming pointer to reference type `int&' Line 80 is the line where operator() is defined. The error message indicates that argument_type is obviously not correct in this context. I thought the boost traits would take care of the right argument types? Am I missing something?
Operation::argument_type is whatever the argument type of the operator is. It is up to you to transform that into whatever argument type you want for your adaptor! In this case, you want to remove a reference (if there is one), and add a pointer. HTH, Ian
Ian McCulloch wrote:
template< typename Operation > class indirecter_unary : public std::unary_function< typename boost::unary_traits<Operation>::argument_type,
Is this correct? You are advertising that your function has an argument_type that is the same as Operation::argument_type. Don't you want your argument_type to be a pointer?
Hm yes, that is true. So let me correct this to: class indirecter_unary : public std::unary_function< typename boost::unary_traits<Operation>::argument_type*, typename boost::unary_traits<Operation>::result_type >
In any event, since you need to redefine argument_type and result_type yourself, there is no point inheriting from std::unary_function.
Why? I thought those types are boost::unary_traits specific types? I thought I have to inherit from std::unary_function if I want to create what is called an "Adaptable Unary Function"?
typename boost::unary_traits<Operation>::result_type > { typedef typename boost::unary_traits<Operation>::argument_type arg_type;
I think, the argument_type you are looking for is
typedef typename boost::remove_reference
::type* argument_type;
Thanks, that sounds reasonable.
typedef typename boost::unary_traits<Operation>::param_type param_type;
IIUC, param_type is supposed to be a type used to pass this function object as a parameter. ie, it should be something like indirecter_unary const&, NOT the param_type of Operation.
Yes, I know. That's how I used it: To pass objects of type Operation to the constructor of indirecter_unary.
typedef typename boost::unary_traits<Operation>::result_type result_type; typedef typename boost::unary_traits<Operation>::function_type function_type; function_type op_; public: explicit indirecter_unary( param_type op ): op_(op) {} result_type operator() (arg_type *arg) const {
^^^^^^^^^^ should be: result_type operator()(argument_type arg) const {
Should it? operator() is supposed to take pointers! Where would the indirection be if I would pass non-pointer types? -- Matthias Kaeppler
Matthias Kaeppler wrote:
Ian McCulloch wrote:
template< typename Operation > class indirecter_unary : public std::unary_function< typename boost::unary_traits<Operation>::argument_type,
Is this correct? You are advertising that your function has an argument_type that is the same as Operation::argument_type. Don't you want your argument_type to be a pointer?
Hm yes, that is true. So let me correct this to:
class indirecter_unary : public std::unary_function< typename boost::unary_traits<Operation>::argument_type*, typename boost::unary_traits<Operation>::result_type >
That will still cause problems with pointers to references.
In any event, since you need to redefine argument_type and result_type yourself, there is no point inheriting from std::unary_function.
Why? I thought those types are boost::unary_traits specific types? I thought I have to inherit from std::unary_function if I want to create what is called an "Adaptable Unary Function"?
No, you only need to provide the typedef's for argument_type and result_type. std::unary_function is just a convenience to save typing. But in your case, it only adds to the typing.
typename boost::unary_traits<Operation>::result_type > { typedef typename boost::unary_traits<Operation>::argument_type arg_type;
I think, the argument_type you are looking for is
typedef typename boost::remove_reference
::type* argument_type; Thanks, that sounds reasonable.
typedef typename boost::unary_traits<Operation>::param_type param_type;
IIUC, param_type is supposed to be a type used to pass this function object as a parameter. ie, it should be something like indirecter_unary const&, NOT the param_type of Operation.
Yes, I know. That's how I used it: To pass objects of type Operation to the constructor of indirecter_unary.
Ok, I misunderstood the first time I read it.
typedef typename boost::unary_traits<Operation>::result_type result_type; typedef typename boost::unary_traits<Operation>::function_type function_type; function_type op_; public: explicit indirecter_unary( param_type op ): op_(op) {} result_type operator() (arg_type *arg) const {
^^^^^^^^^^ should be: result_type operator()(argument_type arg) const {
Should it? operator() is supposed to take pointers! Where would the indirection be if I would pass non-pointer types?
It does - from above:
I think, the argument_type you are looking for is
typedef typename boost::remove_reference
::type* argument_type;
argument_type is a pointer type.
There is still a subtle problem here with what the argument_type should be
when Operator takes is argument by value. indirecter_unary should then
have an argument_type that is a pointer to const. One way to achieve that
would be to use remove_reference
Ian McCulloch wrote:
class indirecter_unary : public std::unary_function< typename boost::unary_traits<Operation>::argument_type*, typename boost::unary_traits<Operation>::result_type >
That will still cause problems with pointers to references.
That is one point where I wasn't yet sure (the compiler also complained about it): Why is it illegal to have pointers to references at all?
There is still a subtle problem here with what the argument_type should be when Operator takes is argument by value. indirecter_unary should then have an argument_type that is a pointer to const. One way to achieve that would be to use remove_reference
::type>::type* as the argument_type.
Are those two function templates also defined in call_traits.hpp? I can't find them in the library docs. -- Matthias Kaeppler
Matthias Kaeppler wrote:
Are those two function templates also defined in call_traits.hpp? I can't find them in the library docs.
type_traits.hpp -- found it :)
Alright, based on your valuable input, I have come up with this (those
template meta programming facilities of boost are /amazing/!):
template< typename Operation >
class indirecter_unary
{
typedef typename boost::unary_traits<Operation>::argument_type arg_type;
typedef typename boost::unary_traits<Operation>::param_type param_type;
typedef typename boost::unary_traits<Operation>::result_type result_type;
typedef typename boost::unary_traits<Operation>::function_type
function_type;
typedef typename boost::remove_reference
Anything else coming to your mind what could be bad about this adaptor? I want it to be as robust as possible because I intend to use it a lot.
You need to add the const declaretor, *after* you remove the reference (adding cv-qualifiers to reference types has no effect). John.
John Maddock wrote:
Anything else coming to your mind what could be bad about this adaptor? I want it to be as robust as possible because I intend to use it a lot.
You need to add the const declaretor, *after* you remove the reference (adding cv-qualifiers to reference types has no effect).
John.
Yes, thanks, I already did that in the meantime (among other corrections). I have posted the new version to the boost developer's list, to also receive their feedback. It's not too positive though... -- Matthias Kaeppler
John Maddock wrote:
Anything else coming to your mind what could be bad about this adaptor? I want it to be as robust as possible because I intend to use it a lot.
You need to add the const declaretor, *after* you remove the reference (adding cv-qualifiers to reference types has no effect).
No, that gives a different effect to what I intended. The task is to take an argument_type that could be either a value or a reference, and convert it to a corresponding pointer type. If the argument_type is a (const or non-const) reference, then simply removing the reference is what is required. Adding const after that would fail for argument_type's that are non-const references as *ptr would be a reference to a const. Conversely, if argument_type is not a reference, then the pointer type should be a pointer to const, because the argument will be copied anyway so there is no reason to forbid using it with const objects. Unless I missed something, adding const followed by removing reference should achieve this. Cheers, Ian
Matthias Kaeppler wrote:
In any event, since you need to redefine argument_type and result_type yourself, there is no point inheriting from std::unary_function.
Why? I thought those types are boost::unary_traits specific types? I thought I have to inherit from std::unary_function if I want to create what is called an "Adaptable Unary Function"?
To define an adaptable unary function, all you need is the member types. One way to acquire them is to inherit from unary_function. If you redefine argument_type and result_type, inheriting from unary function does no good;in fact, it's harmful if the argument_type and result_type don't coincide with the inherited versions, since users may only look at the base class list. Jonathan
Jonathan Turkanis wrote:
Matthias Kaeppler wrote:
In any event, since you need to redefine argument_type and result_type yourself, there is no point inheriting from std::unary_function.
Why? I thought those types are boost::unary_traits specific types? I thought I have to inherit from std::unary_function if I want to create what is called an "Adaptable Unary Function"?
To define an adaptable unary function, all you need is the member types. One way to acquire them is to inherit from unary_function. If you redefine argument_type and result_type, inheriting from unary function does no good;in fact, it's harmful if the argument_type and result_type don't coincide with the inherited versions, since users may only look at the base class list.
Jonathan
Okay, thanks for clearing that up. -- Matthias Kaeppler
participants (4)
-
Ian McCulloch
-
John Maddock
-
Jonathan Turkanis
-
Matthias Kaeppler