Click to See Complete Forum and Search --> : How to encapsulate the "Creation through smart pointers" only interface


PaulWendt
April 16th, 2003, 10:12 AM
I have a series of classes whose creation interface I'd like to be
a static create() function which returns a smart pointer to an
object of the class. I don't want the users creating objects by
themselves so I have made the constructors protected.
Here's a brief example:

class TestClass
{
public:
typedef boost::shared_ptr<TestClass> Ptr;
static Ptr create();

private: // protected if set up for inheritance
TestClass()
// etc
Ptr someDataThatIsOfTypePtr;
};


I may want to add a clone() function or a virtual destructor, but
that is all something that can be discussed later. The problem is
that I have to repeat all of this code for all classes that I want
this interface for. So, I came up with a "wrapper" template for
this sort of thing:


template <typename T>
class SharedPtr
{
public:
typedef boost:shared_ptr<T> Ptr;
static Ptr create() { return Ptr(new T); }
};


If I want TestClass to benefit from this, TestClass has to derive
from SharedPtr<TestClass> because TestClass internally needs
the Ptr typedef [I don't want to have to repeat all of that]. After
I inherit, SharedPtr doesn't even have access to the constructor
[because it's protected/private] so I wind up having to make
SharedPtr a friend. This doesn't seem right; there has to be a
better way.

So ... what the heck should I be doing in order to make an object
creatable only with a smart pointer interface? I'm sure that I've
seen something like this in the past on this site, but a search
proved to be fruitless.

If anybody needs any more details, I will be happy to provide
them.

--Paul

Graham
April 16th, 2003, 10:37 AM
Paul: I think you're actually better off accepting the pain of repeating the code each time. The problem you have with trying to make a general solution is the same that class like ScopeGuard suffers from: annoying class functions (or ctors in your case) that want different numbers of arguments.

Given the SharedPtr you just defined, what do you do with classes that don't have a default ctor? You're going to end up defining all sorts of templated create() functions for varying numbers of arguments. And, of course, you've no way of propagating any default arguments to the create(0 function either, so people have got to explicitly state defaults.

On the whole, I'd leave the create() in the class that's being created where it can be tailored to fit. It's also cleaner. ScopeGuard is OK because it's so useful that you can accept the less-than-trivial implementation, but in your case I'd go for the K.I.S.S. approach.

PaulWendt
April 16th, 2003, 11:37 AM
Thanks a lot for your advice, Graham. I wish that I could come up
with something a little better, but you're right in that it could
potentially be a maintenance nightmare.

Also, I did a quick search on Google and found ScopeGuard to be
something that Andrei Alexandrescu posted on C++ Users
Journal. Is this the class you were referring to? If so, there is no
need to reply; no news is good news :)

--Paul

galathaea
April 16th, 2003, 03:15 PM
I wouldn't be too afraid of the friend in this case. It's not as scary a coupling as the normal use of friend across unrelated types since you would be already using the "curiously recurring template pattern" to provide a form of reverse inheritance coupling, and the headers would already be linked in the translation unit. So it would seem that friend would be exactly what you need to wrap up your creation management idiom nicely.

However, what Graham hath spoken is gospel. It would seem like you may want to use such a creation management system throughout your application's design, and then you run into all sorts of problems in trying to make general a problem that is specific to the ctor's signature and defaults. This makes it very hard to reuse code in a completely generic fashion. However, I normally find that any particular hierarchy of mine normally maintains the same ctor signature and defaults throughout that hierarchy (in fact, I often enforce that as a personal rule because it keeps my hierarchies minimal and is a great indicator for when delegation should replace inheritance). If this is the case in your application as well, there is certainly the possibility for using hierarchy-wide traits to tell you which creation wrapper to use. You then can build up creation wrappers for much larger pieces of your application, and get at least some degree of code reuse...

PaulWendt
April 16th, 2003, 03:25 PM
Thank you, galathea, for your input. A lot of my constructors are,
in fact, similar, but there are some objects who need to be
constructed a little bit differently. I think I'm going to have to
follow the KISS principle ... but at least I'm qualified to do that :)

--Paul