Boost logo

Boost :

From: Grunin, Oleg (Exchange) (ogrunin_at_[hidden])
Date: 2002-04-02 11:20:32


A few years ago I came across an article in c++ users' journal, I believe,
describing how an obscure feature of
 operator ->() can be used to give smart pointers an exra twist. This idea
is to insert custom code before and after each call through the smart
pointer. It can be useful in a number cases (tracing function calls and
locking, for instance) and relies on the fact that the return type of
operator ->() need not be the underlying raw pointer type but rather any
object that in turn provides operator ->(). Those objects objects are
instantiated by the compiler on the fly just before the call is made and
destroyed right after. They can be chained indefinitely as long as the very
last one in the chain does return the real raw pointer.

Here is a simple example that demonstrates that:

#include <iostream.h>
#include <typeinfo.h>
#include <boost/thread/mutex.hpp>

template<class T>
class
tracing_ptr
{
public:
        tracing_ptr(T *p) :ptr_(p){ptr_->precall();}

        ~tracing_ptr(){ptr_->postcall();}

        T * operator-> () {return ptr_;}

        T *ptr_;
};

template<class T, class WRAPPER = T*>
class
locking_ptr
{
public:
                
        locking_ptr(T *p):ptr_(p), lock_(p->get_lock())
        {
                cout << typeid(T).name() << " locked" << endl;
        }

        ~locking_ptr()
        {
                cout << typeid(T).name() << " unlocked" << endl;
        }

        WRAPPER operator ->(){return ptr_;}

private:
        T *ptr_;
        
        T::lock_t::scoped_lock lock_;
};

template<class T, class WRAPPER = T*>
class
wrap_ptr
{
public:
        wrap_ptr(T *p):ptr_(p){}
        
        WRAPPER operator -> (){return ptr_;}

private:
        T *ptr_;
};

struct
Foo
{
        typedef boost::mutex lock_t;
        
        void bar() const
        {
                cout << "bar()" << endl;
        }

        void precall() const
        {
                cout << "precall()" << endl;
        }

        void postcall() const
        {
                cout << "postcall()" << endl;
        }

        lock_t& get_lock() const{return lock_;}

private:
        mutable lock_t lock_;
};

int main(int argc, char **argv)
{
        
        typedef wrap_ptr<Foo, locking_ptr<Foo, tracing_ptr<Foo> > >
foo_ptr_t;

        foo_ptr_t p1(new Foo);
        
        p1->bar();

        return 0;
}
template<class T, class WRAPPER = T*>
class
wrap_ptr
{
public:
        wrap_ptr(T *p):ptr_(p){}
        
        WRAPPER operator -> (){return ptr_;}

private:
        T *ptr_;
};

struct
Foo
{
        typedef boost::mutex lock_t;
        
        void bar() const
        {
                cout << "bar()" << endl;
        }

        void precall() const
        {
                cout << "precall()" << endl;
        }

        void postcall() const
        {
                cout << "postcall()" << endl;
        }

        lock_t& get_lock() const{return lock_;}

private:
        mutable lock_t lock_;
};

int main(int argc, char **argv)
{
        
        typedef wrap_ptr<Foo, locking_ptr<Foo, tracing_ptr<Foo> > >
foo_ptr_t;

        foo_ptr_t p1(new Foo);
        
        p1->bar();

        return 0;
}

the output looks like this:

struct Foo locked
precall()
bar()
postcall()
struct Foo unlocked

This feature may come in handy and can safely be added to boost::share_ptr
since and does not affect any existing code.

****************************************************************
Bear Stearns is not responsible for any recommendation, solicitation,
offer or agreement or any information about any transaction, customer
account or account activity contained in this communication.
***********************************************************************


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