views:

457

answers:

5

I have written a library that exposes references to several related object types. All of these objects have their lifetimes managed by the library internally via boost::shared_ptr

A user of the library would also be able to know, by nature of the library, the lifetimes of any of the exposed objects. So they could store pointers or keep references to these objects. It would be reasonable for them to do this and know when those objects are no longer valid.

But I feel guilty forcing my users to be reasonable.

Is it acceptable to have a library expose weak_ptr's to its objects? Have other libraries done this?

I have profiled this library's usage in apps and have found it to be too mission-critical to expose weak_ptr exclusively.

Would it be wiser to have matching API functions expose either a reference or a weak_ptr or to make any object capable of exposing a weak_ptr to itself?

+1  A: 

I don't see any problem with exposing weak_ptrs, especially given that TR1 has similar smart pointers (PDF).

TR1 is largely implemented by Visual Studio and GCC, but not some of the other compilers out there. But when it's implemented in all the compilers you care about, you may want to rework the API to expose those smart pointers instead.

Max Lybbert
+4  A: 

If the smart_ptrs are already directly accessible to the library's users, then they've already got access to the weak_ptrs, simply via the corresponding weak_ptr's constructor. But if the smart_ptrs are all internal to the library, that's a different story.

In that case, I'd recommend letting each object pass out weak_ptrs to itself, in addition to any other access your library offers. That gives the users the most flexibility: if they need a weak_ptr, they've got immediate access to it; if they need a shared_ptr, they can easily get it; and if they just need access to the object itself, they can ignore the smart pointers entirely.

Of course, I don't know what your library does or how it's used or designed. That might change my recommendation.

Head Geek
The library currently uses shared_ptr internally only.
Shmoopty
+2  A: 

If you give your clients access to weak_ptrs they can just lock them to create shared_ptrs and end up delaying the destruction of objects. That might cause problems with your library.

I suggest wrapping a weak_ptr in some other class and giving the caller a shared_ptr to that. That way they can't just call weak_ptr<T>::lock(). You seem to have performance constraints that might influence how you implement it, but a shared_ptr<InterfaceClass> might be a good way to go, and keep the class with the weak_ptr internal to your library.

That way you also keep these implementation details out of your library interface and you can change the way you implement it without changing your interface.

janm
+4  A: 

Coming up with convoluted mechanisms to get at the objects of your library will only result in people not using your library. If the semantics of the library dictate you need to have people using weak_ptrs, there no way around the user knowing that the objects may go away at some point. Make the interface express as much information about the usage of the library as possible, keeps documentation down and makes it infinitely easier to use.

You can't design around bad/inexperienced users.

Nick Veys
A: 

If you want to both trap invalid use of the library (trying to access the objects when they have been deleted) as well as have a high-performance API (no weak_ptr's and shared_ptr's in the API), then you could consider have a different API for debug and nondebug builds.

Let's suppose for simplicity that you have only one class of objects you expose; call this class Object. The pointer type which you return from the API for accessing the internal objects is then defined as:

#ifdef DEBUG
typedef ObjectPtrFacade ObjectPtr 
#else
typedef Object * ObjectPtr;
#endif

Here the facade is class which you write. It works roughly like this:

class ObjectPtrFacade {
public:
    ObjectPtrFacade(Object *o) : wptr(o) { }
    // copy constructor and assignment here etc. (not written)
    Object * operator -> () const { return access(); }
    Object & operator * () const { return *access(); }
private:
    Object * access() { 
      assert(wptr.use_count() > 0);
      return (Object *)(wptr.lock());
    }
    weak_ptr<Object> wptr; 
}

In this way, whenever you build a debugging build, you have a special kind of smart pointer in use which asserts before accessing the object that its use_count() is higher than zero, i.e. that the object still exists. If the object has been released, you get a failing assert, which is better than a null pointer reference.

In general of course it is so that using weak_ptr's does not help if you have "stupid" users of the API, because they could call lock() and then still make a null-pointer reference after the weak_ptr returns a shared_ptr which is empty...

antti.huima