Thread: Learning functors problem. Pointer to reference

  1. #1
    Registered User
    Join Date
    Oct 2008
    Posts
    4

    Learning functors problem. Pointer to reference

    Hi...I've been playing around trying to learn bind1st, mem_fun, ptr_fun, etc. and I noticed that my code I always have collections of pointers, not objects (maybe this is bad?). So I thought a functor to dereference the pointer and apply the function would be useful e.g.
    Code:
    #include <iostream>
    #include <functional>
    #include <vector>
    
    using namespace std;
    
    // functor to dereference argument before applying the unary_function
    template<class Op>
    class deref_and_apply : public unary_function<typename Op::argument_type*,
                                                  typename Op::result_type>
    {
    public:
        deref_and_apply(const Op& op) : m_op(op) { }
        typename Op::result_type operator() (const typename Op::argument_type* p)
        {
            return m_op(*p);
        }
    protected:
        Op m_op;
    };
    
    template <class Op>
    inline deref_and_apply<Op> derefapply(const Op& op)
    { return deref_and_apply<Op>(op); }
    
    
    int main()
    {
        vector<const int*> l_list1;
        l_list1.push_back(new int(1));
        l_list1.push_back(new int(13));
        l_list1.push_back(new int(42));
    
        vector<const int*>::iterator l_itr = find_if( l_list1.begin(), l_list1.end(),
                derefapply( bind2nd(greater<int>(),7) ) );
        if (l_itr != l_list1.end() )
        {
            cout << "FOUND " << **l_itr << "\n";
        }
    
        return 0;
    }
    This works, but then I tried the following and I found I couldn't stop the copy of the Node object before being passed to the Printer functor. I get a "pointer to reference" error.
    Code:
    #include <iostream>
    #include <functional>
    #include <vector>
    
    using namespace std;
    
    // Simple test class
    struct Node {
        Node(const Node& n) : m_id(n.m_id) { cout << "COPY NODE " << m_id << "\n"; }
        Node(int id) : m_id(id) { }
        int m_id;
    };
    ostream& operator<<(ostream& os, const Node& n) { return os << "NODE " << n.m_id << "\n"; }
    
    // Send object of type T to ostream
    template <typename T>
    class Printer : public unary_function<T, void> {
    public:
        Printer(ostream& rOS) : m_os(rOS) { }
        void operator()(const T t) { m_os << t; }
    protected:
        ostream& m_os;
    };
    
    // functor to dereference argument before applying the unary_function
    template<class Op>
    class deref_and_apply : public unary_function<typename Op::argument_type*,
                                                  typename Op::result_type>
    {
    public:
        deref_and_apply(const Op& op) : m_op(op) { }
        typename Op::result_type operator() (const typename Op::argument_type* p)
        {
            return m_op(*p);
        }
    protected:
        Op m_op;
    };
    
    template <class Op>
    inline deref_and_apply<Op> derefapply(const Op& op)
    { return deref_and_apply<Op>(op); }
    
    int main()
    {
        vector<const Node*> l_list1;
        l_list1.push_back(new Node(1));
        l_list1.push_back(new Node(2));
        vector<Node> l_list2;
        l_list2.push_back(Node(10));
        l_list2.push_back(Node(20));
    
        // This will copy each node before passing to Printer
        for_each(l_list1.begin(), l_list1.end(), derefapply( Printer<const Node>(cout) ));
        //XXX This doesn't compile. Pointer to reference error
        //for_each(l_list1.begin(), l_list1.end(), derefapply( Printer<const Node&>(cout) ));
    
        // This doesn't copy the objects
        for_each(l_list2.begin(), l_list2.end(), Printer<const Node&>(cout) );
        return 0;
    }
    I guess my questions are
    1) Is there a way to get the derefapply to work without a copy of the Node?
    2) In my investigations I got this error a lot. Am I missing something fundamental about how to structure and use this stuff e.g. should I not use collections of pointers?
    3) I notice at the end (just above the conclusion) of this article http://www.ddj.com/cpp/184401746 it mentions problems with references to references and says boost provides better functionality. Am I just looking at this too simplistically and to do this kind of generic programming you need a lot more and smarter code than my simple test?
    Thanks,

    Stuart.

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The copy appears to be caused by Printer::operator() taking the argument by value. Why don't you make it accept it by reference and change how you instantiate the Printer object:

    Code:
    void operator()(const T& t) { m_os << t; }
    
    for_each(l_list1.begin(), l_list1.end(), derefapply( Printer<Node>(cout) ));
    
    for_each(l_list2.begin(), l_list2.end(), Printer<Node>(cout) );
    (In general it seems somewhat strange to me to specify the constness and whether you are using a reference or not in template arguments.)

    Collections of pointers are problematic indeed (you don't free the memory and it is hard to use some algorithms since you'd get no chance to free the memory).

    Boost has ptr_containers whose operator[]/iterators dereferences the pointer for you, if I'm not mistaken, so it should be usable with algorithms without any special efforts.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    4
    The copy appears to be caused by Printer:perator() taking the argument by value.
    Ahh thank you...I actually changed it from a reference as part of my many trial and errors. I'm trying to remember now why I did that.
    (In general it seems somewhat strange to me to specify the constness and whether you are using a reference or not in template arguments.)
    This is the kind of thing question2 was about. I really don't know how this kind of thing should be done. For example, at first my deref_and_apply functor stored the m_op as a non-const reference, but then I hit a problem where I couldn't use an unnamed temporary e.g.
    Code:
        //XXX Doesn't compile
        //for_each(l_list1.begin(), l_list1.end(), derefapply_REF( Printer<const Node>(cout) ));
        // Works
        Printer<const Node> l_printer(cout);
        for_each(l_list1.begin(), l_list1.end(), derefapply_REF( l_printer ) );
    So I concluded that it was the wrong thing to do, but I don't understand why....This whole approach of template, temp objects and functors I find quite a head f**k.

    I also still have a problem with have problem with derefapply and ptr_fun e.g.
    Code:
    static void printNodeRef(const Node& n) { cout << n; }
    static void printNodeObj(const Node n) { cout << n; }
    
        for_each(l_list1.begin(), l_list1.end(),
                //XXX derefapply( ptr_fun(printNodeRef) ) );
                derefapply( ptr_fun(printNodeObj) ) );
    again, it's the "pointer to reference" error. I feel like there are some guidelines about doing this kind of thing that I'm missing. I'm sure I could use the boost libraries and get on great, but I really want to actually understand how these things should be done, not just use a well written library that works "by magic".

    Thanks for the help.

    Stuart.

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    For example, at first my deref_and_apply functor stored the m_op as a non-const reference, but then I hit a problem where I couldn't use an unnamed temporary e.g.
    So I concluded that it was the wrong thing to do, but I don't understand why....
    Yes, you can't bind unnamed temporaries to non-const references. However, it should not be a problem if the functor itself is stored by value (since they should be small and designed to be passed by value).

    again, it's the "pointer to reference" error.
    Well, the cause of the error seems to be that at template instantiation
    Code:
    class deref_and_apply : public unary_function<typename Op::argument_type*,
                                                  typename Op::result_type>
    becomes
    Code:
    class deref_and_apply : public unary_function<const Node&*, void>
    which is illegal.

    I'm no master in these quarters but it seems that it would be possible to throw in yet another metaclass that removes the reference'ness from the type through partial specialization. (I didn't test, whether there might be problems with constness as well.)

    Try the following. It shouldn't create any unnecessary copies (except the last call copies each object twice - not sure if it is because the argument goes through several wrappers):

    Code:
    #include <iostream>
    #include <functional>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    // Simple test class
    struct Node {
        Node(const Node& n) : m_id(n.m_id) { cout << "COPY NODE " << m_id << "\n"; }
        Node(int id) : m_id(id) { }
        int m_id;
    };
    ostream& operator<<(ostream& os, const Node& n) { return os << "NODE " << n.m_id << "\n"; }
    
    // Send object of type T to ostream
    template <typename T>
    class Printer : public unary_function<T, void> {
    public:
        Printer(ostream& rOS) : m_os(rOS) { }
        void operator()(const T& t) { m_os << t; }
    protected:
        ostream& m_os;
    };
    
    template <class T>
    struct ref_stripper
    {
        typedef T type;
    };
    
    template <class T>
    struct ref_stripper<T&>
    {
        typedef T type;
    };
    
    // functor to dereference argument before applying the unary_function
    template<class Op>
    class deref_and_apply : public unary_function<typename ref_stripper<typename Op::argument_type>::type*,
                                                 typename Op::result_type>
    {
    public:
        deref_and_apply(const Op& op) : m_op(op) { }
        typename Op::result_type operator() (const typename ref_stripper<typename Op::argument_type>::type* p)
        {
            return m_op(*p);
        }
    protected:
        Op m_op;
    };
    
    template <class Op>
    inline deref_and_apply<Op> derefapply(const Op& op)
    { return deref_and_apply<Op>(op); }
    
    void printNodeRef(const Node& n) { cout << n; }
    void printNodeObj(const Node n) { cout << n; }
    
    int main()
    {
        vector<const Node*> l_list1;
        l_list1.push_back(new Node(1));
        l_list1.push_back(new Node(2));
        vector<Node> l_list2;
        l_list2.push_back(Node(10));
        l_list2.push_back(Node(20));
    
        for_each(l_list1.begin(), l_list1.end(), derefapply( Printer<Node>(cout) ));
    
        for_each(l_list2.begin(), l_list2.end(), Printer<Node>(cout) );
    
        std::cout << "PrintNodeRef\n";
        for_each(l_list1.begin(), l_list1.end(),
                derefapply( ptr_fun(printNodeRef) ) );
        std::cout << "PrintNodeObj\n";
        for_each(l_list1.begin(), l_list1.end(),
                derefapply( ptr_fun(printNodeObj) ) );
        return 0;
    }
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    Registered User
    Join Date
    Oct 2008
    Posts
    4
    Code:
    typename ref_stripper<typename Op::argument_type>::type*
    Nice! Is there a good text on learning this stuff? My C++ is pretty good, and I can understand how these things work, but there is no way I would have come up with the above solution and just "hacking around" and browsing the web doesn't look like it's going to teach me.
    Thanks again.
    --Stuart.

    PS. Nice sig

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Is there a good text on learning this stuff?
    "C++ Templates" by Vandeovoorde and Josuttis, and "C++ Template Metaprogramming" by Abrahams and Gurtovoy.

    The first is the definite guide to all the dark corners of template handling, whereas the second is a great book about the more complex techniques, such as type traits. (ref_stripper is basically remove_reference from the type traits, for example.)
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. sorting number
    By Leslie in forum C Programming
    Replies: 8
    Last Post: 05-20-2009, 04:23 AM
  2. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  3. Replies: 0
    Last Post: 03-20-2008, 07:59 AM
  4. pointer problem or so...
    By TL62 in forum C Programming
    Replies: 19
    Last Post: 01-12-2008, 11:45 PM
  5. Direct3D problem
    By cboard_member in forum Game Programming
    Replies: 10
    Last Post: 04-09-2006, 03:36 AM

Tags for this Thread