The Artima Developer Community
Sponsored Link

Java Buzz Forum
operator->() ad infinitum

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
noe casas

Posts: 14
Nickname: noecasas
Registered: Feb, 2009

Noe Casas is a knowledge-hungry software engineer
operator->() ad infinitum Posted: Feb 1, 2009 7:03 PM
Reply to this message Reply

This post originated from an RSS feed registered with Java Buzz by noe casas.
Original Post: operator->() ad infinitum
Feed Title: SoftwareDesignStuff
Feed URL: http://feeds2.feedburner.com/softwaredesignstuff
Feed Description: This blog is a place to share my ideas and opinions on topics like software design, good/bad programming practices, design patterns, programming languages, frameworks, software technology and so on, and to discuss them with you.
Latest Java Buzz Posts
Latest Java Buzz Posts by noe casas
Latest Posts From SoftwareDesignStuff

Advertisement
Short version:

In C++, if an implementation of operator->() returns something different than a raw pointer, operator->() will be invoked on that return value; this will happen again and again until at some point a raw pointer is returned.


Lil' longer version:

One of the operators C++ allows to be overloaded is "->" . This enables syntactic constructions that mimic pointer usage, like smart pointers, which look like this...


template <typename T>
class MySmartPointer {
public:
MySmartPointer(T* pointee) : pointee_(pointee) {}

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

/* some magic here... */

private:
T* pointee_;
};


Thus, you can use MySmartPointer with the same syntax as a plain T* :


MyClass* x (new MyClass);
x->doStuff();

MySmartPointer <MyClass> y (new MyClass);
y->doStuff();


However, if we declare operator->() to return something that is not a raw pointer, the compiler will generate code to invoke operator-> on that object, and so on:


struct Baz {
void sayHello() { cout << "hello" <<>() { return baz_; }
};
struct Bar {
Baz* baz_;
Baz* operator->() { return baz_; }
};
struct Foo {
Bar bar_;
Bar operator->() { return bar_; }
};

void doStuff () {
Foo foo;
foo->sayHello();
}



'doStuff' is equivalent to this:

void doStuff () {
Foo foo;
Bar bar (foo.operator->());
Baz* baz = bar.operator->();
baz->sayHello();
}


Bjarne Stroustrup used this language feature -plus a stack allocated auxiliary object- in a proposal for a C++ idiom for wrapping calls to member functions, enabling to perform stuff just before and just after any method call (i.e. intercept it). Let's see it in an example:


template<typename T>
class LockSmartPtrAux {
public:
LockSmartPtrAux(T* t, Lock& lock)
: t_(t), lock_(lock) {
lock_.lock(); /* lock at creation */
}
~LockSmartPtrAux() {
lock_.unlock(); /* unlock at destruction */
}
T* operator->() { return t_; }
private:
T* t_;
Lock& lock_;
};

template<typename T>
class LockSmartPtr {
public:
LockSmartPtr(T* t) : t_(t) {}
LockSmartPtrAux<T> operator->() {
return LockSmartPtrAux<T>(t_, lock_);
}
private:
T* t_;
Lock lock_;
};
// LockSmartPtrAux should be declared inside LockSmartPtr to enhance symbol locality


With the class templates above, you can "wrap" any class so that every invocation to its member functions is guarded by a lock, thus achieving some degree of transparent thread safety:


map<string, MyClass> cache;
/* fill cache with useful and hard to build stuff... */
LockSmartPtr<map<string,MyClass> > threadsafeCache(&cache);
doSomethingInAnotherThread1 (threadsafeCache);
doSomethingInAnotherThread2 (threadsafeCache);


Here follow some more details about the implementation...

  • To just perform some action (e.g. lock_.lock() ) BEFORE the "intercepted" call, you don't need an extra auxiliary object (e.g. LockSmartPtrAux), but only to invoke it before returning the pointee.

  • However, to perform some action (e.g. lock_.unlock() ) just AFTER the intercepted call and before the return, you have to place that action in the destructor of an auxiliary object and use operator-> trick.



This technique can be used to implement a rudimentary form of Aspect Oriented Programming in C++ by expressing cross-cutting concerns in these "wrapper" classes.

That's all the C++ trickery for today, hope you liked it.

NOTE: my implementation of LockSmartPtr assumes your compiler implements the name return value optimization (NRVO); if this does not hold true and you use non recursive locks, you will probably get a nasty deadlock due to temporary copies of LockSmartPtrAux locking an already locked mutex from the same thread. Check out Stroutstrup's paper to see how (managing lock ownership, forbidding assignment) he tackles this and other issues.

Read: operator->() ad infinitum

Topic: Let Me Entertain You Previous Topic   Next Topic Topic: Windows 7 Guide: How to Run Windows 7 in a Virtual Partition

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use