Page 1 of 1

Abstract and Dynamic Factories destroy instances

Posted: 17 Mar 2009, 18:38
by gregoire
Currently the creation of objects is managed by the Factory via the createInstance method.
I was wondering if the deletion of this object should not be carried out by the Factory too, so that creation and deletion are managed in the same compilation unit, to avoid problems when linking to a dynamic library (in a plugin system for example).

So my question is:

Code: Select all

//Is it safer to do :
Base* b = myInstantiator.createInstance();
delete b;

//or
Base* b = myInstantiator.createInstance();
myInstantiator.destroyInstance(b);

Re: Abstract and Dynamic Factories destroy instances

Posted: 17 Mar 2009, 21:00
by alex
gregoire wrote:I was wondering if the deletion of this object should not be carried out by the Factory too, so that creation and deletion are managed in the same compilation unit, to avoid problems when linking to a dynamic library (in a plugin system for example).


This is a concern only if you are mixing different MSVC compiler versions (or mix release and debug code) - then you end up with different heaps for the DLLs. Even if we were to provide destroy() methods for all classes potentially affected by this, what are we to do with shared pointers? The only solution, then, remains having everything compiled with the same compiler.

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 12:14
by chrisjones
This is a concern only if you are mixing different MSVC compiler versions (or mix release and debug code) - then you end up with different heaps for the DLLs. Even if we were to provide destroy() methods for all classes potentially affected by this, what are we to do with shared pointers? The only solution, then, remains having everything compiled with the same compiler.

Can't that be solved using the SharedPtr release policy template parameter?

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 14:01
by alex
chrisjones wrote:Can't that be solved using the SharedPtr release policy template parameter?


There's two things you're dealing with here: allocation and deallocation. ReleasePolicy only addresses the latter. It does not guarantee where the pointer was allocated. You'd have to turn SharedPtr into a factory and ban users from using new.

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 15:32
by chrisjones
There's two things you're dealing with here: allocation and deallocation. ReleasePolicy only addresses the latter. It does not guarantee where the pointer was allocated. You'd have to turn SharedPtr into a factory and ban users from using new.

Isn't that up to the programmer? If your designing your application to new and delete in the same module then SharedPtr doesn't have to guarantee where it will be allocated. Factories create the instances, they should probably be able to destroy them too.

What about compiling as static libraries and using the static runtime (rather than the DLL version)? Doesn't that give different heaps meaning you must allocate and deallocate in the same module?

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 17:24
by alex
chrisjones wrote:Isn't that up to the programmer? If your designing your application to new and delete in the same module then SharedPtr doesn't have to guarantee where it will be allocated. Factories create the instances, they should probably be able to destroy them too.


I was not arguing whether a factory that returns pointer to heap-allocated memory should or not have a destroy() member. I do not even disagree that it would make sense that it does. My point was that new/delete across DLL boundaries is a tough problem to solve and introducing said member function is not enough to solve it. What are we, e.g., to do with Notifications where the recipient is responsible for freeing the memory by design?

chrisjones wrote:What about compiling as static libraries and using the static runtime (rather than the DLL version)? Doesn't that give different heaps meaning you must allocate and deallocate in the same module?


Same as with DLLs - if you are mixing binaries linked with different runtime library versions ("versions" as in 7.1, 8.0 etc as well as release/debug or single/multi-threaded), you will have different heaps. See this MSDN blog post.

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 19:57
by gregoire
I am building a plugin system:
do I have to force everybody that might develop plugins to use the same version of MSVC ?
or
if by design plugins are responsible of new/delete operations on the classes they add to my Factory, can I let them use the one they want (with same compiling options: \MD, etc) ?

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 20:53
by alex
gregoire wrote:I am building a plugin system:
do I have to force everybody that might develop plugins to use the same version of MSVC ?


As things currently stand, yes. Either that or you and your users must be very careful not to new/delete across library boundaries. If you ask for advice, I'd force users to compile with same version of MSVC or, if that is not reasonable to expect, then you provide them with POCO binaries compiled with different MSVC versions/options so they can choose the ones matching their environment.

gregoire wrote:if by design plugins are responsible of new/delete operations on the classes they add to my Factory, can I let them use the one they want (with same compiling options: \MD, etc) ?


I'm not quite clear what do you mean by "add to my Factory". Factory is supposed to produce objects and is typically responsible to call new.

With regards to memory management, if you can assure that there is absolutely no new-ing in one module and delete-ing in another, it does not matter - you can mix and match modules at will. For as long as new and delete are called in the same module they are dealing with the same heap. Once you split them and cross the module boundary (i.e. call new in one module and delete in another) all modules must be linked against the same runtime or else you will have undefined behavior. IMO, containment of new/delete within a module will be very hard to achieve so I recommend using the same runtime and same version of MSVC compiler as the safest strategy.

To elaborate a bit (Guenter please comment if you are following) I've looked into what we have and as it currently stands, SharedPtr will call delete in client's module (ReleasePolicy is a template in header), while AutoPtr will call delete in Foundation (RefCountedObject::release() is defined in implementation file). If we are to eliminate naked pointers from the interfaces in 1.4, it could be beneficial to unify this by defining RefCountedObject in the header. Still, though, this would not eliminate but only alleviate the problem by making it less likely to happen. To entirely shield ourselves from this problem, designs like the one for Notifications, where framework is passing both pointers and responsibility to release the memory between clients (which may be in different modules), should be addressed. One scenario could be banning new/delete and naked pointers altogether, provide allocation/deallocation within the framework (i.e. inside smart pointers, which can not be templates) and require all POCO libraries to be linked against the same runtime. There may be some things eluding me at the moment, but I don't think this goal is worth pursuing (i.e. feasible).

Re: Abstract and Dynamic Factories destroy instances

Posted: 18 Mar 2009, 21:24
by guenter
Dynamic memory is not your only problem when developing a plugin system in C++. ABI compatibility is another major issue if you're going to pass STL objects, or generally, C++ objects across borders. Also, as soon as you start to pass std::strings or other STL objects around, you have to be very careful, as to control the memory allocations/deallocations that might happen in them.