Design Woes.....

M

ma740988

I was relieved somewhat when I saw a post by Andrei Alexandrescu
highlighting an similar issue I'm faced with:
So here's what Andrei Alexandrescu wrote:
[start]
I code a generic object factory. Obviously, the factory has a member
function like CreateObject that returns a newly allocated object.

The problem is, I break an important coding standard: never return
pointers to newly allocated memory. Never pass ownership by returning
pointers. Never do what strdup does. And never means never.
[/stop]

I suspect I should be concerned about breaking the coding standards but
I haven't got that far yet. For simplicity, I'll highlight the stuff -
per the source below - that counts. So now consider:

#ifndef FACTORY_H
#define FACTORY_H
# include <map>
# include <string>

struct stream;
class source //
{
public:
virtual bool load_from_stream(stream& ds) = 0;
virtual void execute() = 0;
};
//////////////////////////////­//////
// factory
class ClassFactory
{
public:
virtual source* create()=0;
virtual std::string name()=0;
};

template < class T >
class FactoryWrapper
: public ClassFactory {
public:
virtual source* create(){
return T::create();
}
virtual std::string name(){
return T::name();
}
};

class Factories
{
std::map<std::string, ClassFactory*> _facts;
public:
void registerFactory( ClassFactory* factory);
source* create(const stream& ds);
};

#endif;

struct test
: public source
{
static std::string name() {
return "test";
}
static test* create(){ return new test();}
};

// now we're ready. main.cpp
class testFact {
FactoryWrapper<test> class1Factory;
Factories factories;

// test m_test; // would like to copy ptest to an m_test or ..
public:
testFact () {
factories.registerFactory(&cla­ss1Factory);
}

void record()
{
// use ptest or some variant
//ptest
}

void get_data()
{
char buffer[4096] = { "testing" };
size_t len = sizeof buffer / sizeof *buffer;


data_stream st (buffer, len);
if (st)
{
test *ptest =
static_cast<test*>(factories.c­reate(st));
if ( !ptest ) {
// do somethign with ptest
delete ptest;
}
}
}
};

Each call to create within get_data results in a call to memory that
must be freed. Trouble is: I need to use the ptest object elsewhere.
For instance within the record member function. That said, how does one
go about creating a local copy of the object for use elsewhere?

If I copy ptest to another object of type test, that's unsafe since the
memory will be freed by ptest.

Having said that. In an attempt to 'meet' coding standards, I suspect
I could pass a test object to the create function. I'm not so sure I
understand what this solves or I'm fully understanding how to achieve
this.

Thanks in advance.
 
P

Phlip

ma740988 said:
The problem is, I break an important coding standard: never return
pointers to newly allocated memory. Never pass ownership by returning
pointers. Never do what strdup does. And never means never.

There's a more encompassing guideline: Each time you code 'new', immediately
account for the 'delete'.

That affects your behavior as a programmer. When you type this, type that
too, very soon. And it leads to the rule you cite, if the client of that
interface might not be written yet, so you can't account for the delete.
class ClassFactory
{
public:
virtual source* create()=0;

struct test: public source

Could we see a real test case here, like from CPPUnit? ;-)
static test* create(){ return new test();}

Right. Now can't this return an auto_ptr too, of a type covariant with the
base class's type?

Also look at the Boost shared_ptr.
 
M

ma740988

| Right. Now can't this return an auto_ptr too, of a type covariant
with the base class's type?

Thus far it sounds like I'll need:
virtual std::auto_ptr<source*> create()=0;

I realize the auto_ptr approach has the built in semantics to do
automatic cleanup. From the sound of it I'll review the two approaches
you highlighted.
but here's the more important question. The returned object needs to
be available for use elsewhere. How do you approach this? Again.
Withiin the get_data member function, a ptest object is created. I'd
like to keep a local copy for use in multiple places - for instance the
record function illustrated above.
 
D

davidrubin

| Right. Now can't this return an auto_ptr too, of a type covariant
with the base class's type?

Thus far it sounds like I'll need:
virtual std::auto_ptr<source*> create()=0;

I think it's just 'std::auto_ptr<source>' which gives you a managed
pointer to a 'source' object. You can use the auto_ptr object as a
pointer, or you can get or release the pointed-to object (the 'source')
from it.
I realize the auto_ptr approach has the built in semantics to do
automatic cleanup. From the sound of it I'll review the two approaches
you highlighted.
but here's the more important question. The returned object needs to
be available for use elsewhere. How do you approach this? Again.
Withiin the get_data member function, a ptest object is created. I'd
like to keep a local copy for use in multiple places - for instance the
record function illustrated above.

Since the 'testFact' class does not maintain a pointer to memory
allocated by 'get_data', you have to pass a parameter to 'record' in
any event. In this case, you can pass a modifiable reference to
'std::auto_ptr<source>'. Thus, 'record' is able to own the object or
not (since the parameter is modifiable).

However, if you are going to keep a local copy of the memory allocated
by 'get_data', the whole auto_ptr argument becomes moot because your
'testFact' object can manage its own memory.

Incidentally, it is a good idea for a factory (protocol) to also
provide 'deallocate' and 'deleteObject' methods. This way, factory
implementations are free to allocate memory from places other than the
heap (for example, from pools or buffers). However, you can no longer
use 'auto_ptr' because you cannot associate a factory with it; it only
knows about new/delete. That is another problem for another day...

/david
 
M

ma740988

| think it's just 'std::auto_ptr<source>' which gives you a managed
| pointer to a 'source' object. You can use the auto_ptr object as a
| pointer, or you can get or release the pointed-to object (the
'source')
| from it.

Even with auto_ptr, each call to get_data() will still require a
delete/freeing of the source object. Correct? Since the source object
is allocating memory on each call to create I'll need to release the
memory. I'm not sure what auto_ptr is buying me but I'll need to
research more.


| However, if you are going to keep a local copy of the memory
allocated
| by 'get_data', the whole auto_ptr argument becomes moot because your
| 'testFact' object can manage its own memory.

Agreed

| Incidentally, it is a good idea for a factory (protocol) to also
| provide 'deallocate' and 'deleteObject' methods.

Agreed. Ideally, I'd like for the 'factory' to delete the allocated
memory _ONLY_ after I get a copy for use in testFact. So now I need
to figure out a way to do::

class testFact {
test m_test;
public:

void get_data()
{
factories.c­­reate(st, &m_test); // pass in m_test object.
Have factory create test object and assign to m_test.
// Factory should
also delete test object
}

// use m_test in all the member functions that need it.
void record()
{
if (m_test.whatever == 5) {}
}
};

How would you do this?
 
M

ma740988

I have one more question: Before I had,

test *ptest =
static_cast<test*>(factories.c­­reate(st));
if (ptest)


Now I'm made all the auto_ptr mods and I'm trying to do:

std::auto_ptr said:
(factories.create(st)));


: error C2440: 'initializing' : cannot convert from 'source *' to 'test
*'

reinterpret_cast (dont think dynamic_cast applied, nonetheless tried it
too to no avail) doesn't work here either so .....
 
D

davidrubin

You would do this using a shared pointer. Shared pointers are
reference-counted, so when the last existing shared pointer object is
destroyed, the memory contained in the shared pointer (i.e., that which
is allocated in 'get_data' by your factory) is returned to the
allocator (or factory, whic is in this case the new/delete
"allocator"). /david
 
K

Kristo

You would do this using a shared pointer. Shared pointers are
reference-counted, so when the last existing shared pointer object is
destroyed, the memory contained in the shared pointer (i.e., that which
is allocated in 'get_data' by your factory) is returned to the
allocator (or factory, whic is in this case the new/delete
"allocator"). /david

What's a shared pointer?

Kristo
 
K

Karl Heinz Buchegger

Kristo said:
What's a shared pointer?

You allocate memory.
You hand out multiple pointers to that memory
All those pointers know how many of them exist. They
'share' the memory resource such that the last of them
that gets destroyed also deletes the memory.
 

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

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,125
Latest member
VinayKumar Nevatia_
Top