From: Jonathan W. <co...@co...> - 2004-10-18 16:54:55
|
Inspecting the code I think I've found a problem, or at least an unnecessary complication. Firstly, Allocator_Zeroed is only suitable for allocating certain types, since it creates a new instance and then zeroes its bytes. This is used to allocate objects of type MYSQL and initialise them to zero. This makes assumptions about struct MYSQL and is not portable if the struct contains a pointer, since there's no guarantee that the NULL pointer is represented by an all-zero bit pattern (although on most real-world systems it is). I think the only _truly_ portable way to initialise a pointer is by assinging 0 to it. This code shows how that allocator breaks a class' invariants: #include <cassert> #include "mysqlcppapi/smartpointer/SharedPtr.h" struct Five { const int i; Five() : i(5) {} }; using namespace mysqlcppapi; int main() { SharedPtr<Five, Allocator_Zeroed<Five> > sp; assert( sp->i == 5 ); } This problem stems from the fact that a default constructed SharedPtr contains a default constructed pointee, rather than being NULL. This requirement means that the pointee type must be default constructable and must not mind being zeroed if Allocator_Zeroed is used. Allocator_Connection uses Allocator_Zeroed, and so contains the above problem. Since the zeroed MYSQL structure is passed immediately to mysql_init() (which initialises it correctly) there is no danger of using the zeroed (incorrectly initialised) structure. Therefore it's not wrong, per se - but it smells a bit. I would prefer not to have a template that is only safe to use for POD types. I believe this could be avoided by making Allocator_Connection not use Allocator_Zeroed at all, but let mysql_init() allocate the structure. This would mean that mysql_close() must be called unconditionally in deallocate(), so the structure will be deallocated. This would greatly simplify the SharedPtr code by removing the 2-stage allocate+initialise combination, and therefore remove the need for the Do2ndStageDeallocation feature. deallocate() would call mysql_close() whether the connection was opened or not (which is correct, since it must free the memory that mysql_init() reserved). The attached patch shows basically what I mean, but is not complete (*) Comments, thoughts? jon (*) the SharedPtr class still stores the do2ndStageDeallocation member, it just doesn't pass it to the allocator's deallocate() function ... ... changing that would mean the Connection can't tell if it's connected, but that's easy to change by adding a new shared_ptr<bool> in Connection ... ... but since Collection is basically an aggregation of several shared_ptr objects we could simplify it further by making Collection just a wrapper around a single shared_ptr<Collection::Impl> and make Collection::Impl hold actual types e.g. class Collection { // ... class Impl; // fwd decl shared_ptr<Impl> m_pimpl; }; class Collection::Impl { // ... std::string unixsocketstr; int port; int timeout; bool locked; bool success; bool connected; }; but this is all for later discussion. -- To conquer oneself is a greater task than conquering others. - Buddha |