smart pointer patterns

C

corophoria

Some people I interact with, especially those with a c background,
have an aversion to scoped_ptrs because they see a new(), but they
don't see a corresponding delete(), and they get very scared

e.g. usage

Class Bar
{
int m_data;
};

void Foo()
{
scoped_ptr<Bar> bar( new Bar() );
process( bar );
}

In the default constructor case, we can make a wrapper around
scoped_ptr like:

template <typename T>
class ScopedWrapper
{
public:
ScopedWrapper<T>
: m_ptr( new T() )
{
}
private:
scoped_ptr<T> m_ptr;
};

then, for the previous function, we can equivalenty write:

void Foo()
{
ScopedWrapper<Bar> bar;
process( bar );
}
Now, people don't have to see the new call, and it's a bit easier for
them to read,

Now, let's say Bar has some arbitrary constructor with signature like:

Bar::Bar(int, double, std::string );

now, to instantiate a Bar using a scoped_ptr i would traditionally do

scoped_ptr<Bar> bar( new Bar(3, 2, "fool") );

Now, what I would like to be able to do is write something like

ScopedWrapper<Bar> bar(3,2,"fool"); or something like that...

I am not sure that really explained what I want. I would like to put
arbitrary objects with arbitrary constructors on the heap rather than
on the stack, without any new/delete's but have some kind of auto
ownership mechanisms?

Sorry if this question has been answered before, or if the C++ syntax
is wrong.

Thanks,
corophoria
 
R

red floyd

Some people I interact with, especially those with a c background,
have an aversion to scoped_ptrs because they see a new(), but they
don't see a corresponding delete(), and they get very scared

e.g. usage

Class Bar
{
int m_data;
};

void Foo()
{
scoped_ptr<Bar> bar( new Bar() );
process( bar );
}

In the default constructor case, we can make a wrapper around
scoped_ptr like:

template <typename T>
class ScopedWrapper
{
public:
ScopedWrapper<T>
: m_ptr( new T() )
{
}
private:
scoped_ptr<T> m_ptr;
};

then, for the previous function, we can equivalenty write:

void Foo()
{
ScopedWrapper<Bar> bar;
process( bar );
}
Now, people don't have to see the new call, and it's a bit easier for
them to read,

Now, let's say Bar has some arbitrary constructor with signature like:

Bar::Bar(int, double, std::string );

now, to instantiate a Bar using a scoped_ptr i would traditionally do

scoped_ptr<Bar> bar( new Bar(3, 2, "fool") );

Now, what I would like to be able to do is write something like

ScopedWrapper<Bar> bar(3,2,"fool"); or something like that...

I am not sure that really explained what I want. I would like to put
arbitrary objects with arbitrary constructors on the heap rather than
on the stack, without any new/delete's but have some kind of auto
ownership mechanisms?

Sorry if this question has been answered before, or if the C++ syntax
is wrong.

What's wrong with specializing ScopedWrapper?

template<>
class ScopedWrapper<Bar> {
ScopedWrapper() : m_ptr(new Bar()) { }
ScopedWrapper(int x, int y, const std::string& z)
: m_ptr(new Bar(x, y, z) { }
};


Alternatively, you might try to use (multiple) templated constructors if
you don't want to do too many specializations.
 
C

corophoria

What's wrong with specializing ScopedWrapper?
template<>
class ScopedWrapper<Bar> {
ScopedWrapper() : m_ptr(new Bar()) { }
ScopedWrapper(int x, int y, const std::string& z)
: m_ptr(new Bar(x, y, z) { }

};

Alternatively, you might try to use (multiple) templated constructors if
you don't want to do too many specializations.

If somebody introduces a new class to the codebase, then I would like
them to be able to use ScopedWrapper without writing any extra lines
of code.

Thanks,
corophoria
 
A

Alf P. Steinbach

* (e-mail address removed):
[snip]
template <typename T>
class ScopedWrapper
{
public:
ScopedWrapper<T>
: m_ptr( new T() )
{
}
private:
scoped_ptr<T> m_ptr;
};
[snip]

Now, what I would like to be able to do is write something like

ScopedWrapper<Bar> bar(3,2,"fool"); or something like that...

Support for argument forwarding is in the works.

Meanwhile, while waiting for language support, you can always use a macro:

#define SCOPED_PTR( basetype, name, args ) \
ScopedWrapper<basetype> name( new basetype args )

then use it like

SCOPED_PTR( Bar, foo,(3, 2, "genius") );

or for default construction,

SCOPED_PTR( Bar, foo,() );

I've always wondered whether that last one is formally valid C++.
However, it's always worked. So in practice there's no problem. :)

Cheers,

- Alf
 
Z

Zeppe

then, for the previous function, we can equivalenty write:

void Foo()
{
ScopedWrapper<Bar> bar;
process( bar );
}
Now, people don't have to see the new call, and it's a bit easier for
them to read,

It seems that you adding complexity in order to facilitate the life of
somebody else, that doesn't want to be up-to-date. Anyway, I see
something in ScopedWrapped that I don't like, namely objects are created
implicitly in the constructor. You may not want to do this always. For
example, what if you want a ScopedPointer that is NULL? And for some of
the operation, for example reset, the problem is still here (though i
know they are not often used...)
Now, let's say Bar has some arbitrary constructor with signature like:

Bar::Bar(int, double, std::string );

now, to instantiate a Bar using a scoped_ptr i would traditionally do

scoped_ptr<Bar> bar( new Bar(3, 2, "fool") );

Now, what I would like to be able to do is write something like

ScopedWrapper<Bar> bar(3,2,"fool"); or something like that...

Unfortunately, as others said, this generalisation is not possible in
C++. The closest thing you can do is providing the class with some
template constructor accepting up to N parameters. This solution seems
bad, but it's actually used in a number of situations. It's very
unlikely that the constructor that you are embedding has got more than,
for example, 10 arguments. Otherwise, with a marco, but it's a little
bit ugly in my opinion.

Regards,

Zeppe
 
A

Alf P. Steinbach

Reposted because unreliable news-server didn't propagate. Of course it
may choose to do so now, so that there'll be two articles. Grr.

* (e-mail address removed):
[snip]
template <typename T>
class ScopedWrapper
{
public:
ScopedWrapper<T>
: m_ptr( new T() )
{
}
private:
scoped_ptr<T> m_ptr;
};
[snip]

Now, what I would like to be able to do is write something like

ScopedWrapper<Bar> bar(3,2,"fool"); or something like that...

Support for argument forwarding is in the works.

Meanwhile, while waiting for language support, you can always use a macro:

#define SCOPED_PTR( basetype, name, args ) \
ScopedWrapper<basetype> name( new basetype args )

then use it like

SCOPED_PTR( Bar, foo,(3, 2, "genius") );

or for default construction,

SCOPED_PTR( Bar, foo,() );

I've always wondered whether that last one is formally valid C++.
However, it's always worked. So in practice there's no problem. :)

Cheers,

- Alf
 
J

James Kanze

Some people I interact with, especially those with a c background,
have an aversion to scoped_ptrs because they see a new(), but they
don't see a corresponding delete(), and they get very scared

How often do you want a new and a delete in the same scope
anyway? In my experience, the case is rare.
e.g. usage
Class Bar
{
int m_data;
};
void Foo()
{
scoped_ptr<Bar> bar( new Bar() );
process( bar );
}

The usual way to write Foo in C++ would be:

void
Foo()
{
Bar bar ;
process( bar ) ;
}

About the only time you'll use new for a local variable is when
polymorphism is involved, and the real type actually varies at
runtime. E.g.:

void
Foo()
{
Base * p = someCondition
? static_cast< Base* >( new Derived1 )
: static_cast< Base* >( new Derived2 ) ;
// ...
}

In such cases, I would definitely use a scoped_ptr if I needed
to call the destructor, or if I wasn't using the Boehm collector
for some reason. Not doing so is just looking for trouble.
In the default constructor case, we can make a wrapper around
scoped_ptr like:
template <typename T>
class ScopedWrapper
{
public:
ScopedWrapper<T>
: m_ptr( new T() )
{
}
private:
scoped_ptr<T> m_ptr;

};
then, for the previous function, we can equivalenty write:
void Foo()
{
ScopedWrapper<Bar> bar;
process( bar );}
Now, people don't have to see the new call, and it's a bit easier for
them to read,

You mean more confusing, since they don't see that the object is
allocated dynamically. And again, the only reason to allocate
dynamically here is if the dynamic type varies. Something your
wrapper won't handle.
Now, let's say Bar has some arbitrary constructor with signature like:
Bar::Bar(int, double, std::string );

That's easy. Just a set of template constructors for your
ScopedWrapper. Try handling my example above, however. Or
something like:

scoped_ptr< Base > p = factoryMap[ someArg ]->create() ;

(where factoryMap is an std::map< ArgType, AbstractFactory
const* >). Cases where you really would use a pointer for an
object whose lifetime corresponds to a scope.

IMHO, you have two problems which need solving:

-- your use of dynamic allocation is not appropriate, and
-- your collegues need to be educated---it's perfectly normal
to see a new and the delete to be elsewhere. (I'd guess
that for well over half of the objects I allocate
dynamically, the delete is in a member function.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,156
Messages
2,570,878
Members
47,404
Latest member
PerryRutt

Latest Threads

Top