Using shared pointers with memory allocated in another function

I have code from an old era at work that takes a double pointer and allocates memory for it. A shortened example would look something like this:

struct LegacyObj { int a; double b; }; void LegacyAllocator(LegacyObj** ppObj) { *ppObj = (LegacyObj*)malloc(sizeof(LegacyObj)); } void LegacyDeleter(LegacyObj** ppObj) { free(*ppObj); } 

The actual function of LegacyAllocator is ~ 100 lines and mixes reading from files to create a linked list of LegacyObj pointers, and this is not something I could leave with rewriting right now. However, I would like to use this function a little safer, avoiding memory leaks that might occur due to & tc exceptions. The first solution I came across was to wrap it in a class and handle calls to inherited functions in ctor / dtor.

 class RAIIWrapper { public: RAIIWrapper() :obj{nullptr} { ::LegacyAllocator(&obj); } RAIIWrapper(RAIIWrapper&& that) : obj{ that.obj} { that.obj = nullptr; } RAIIWrapper& operator=(RAIIWrapper&& that) { RAIIWrapper copy{std::move(that)}; std::swap(obj, copy.obj); return *this; } ~RAIIWrapper () { ::LegacyDeleter(&obj); } private: LegacyObj* obj; }; 

But I'm curious - is there a way to do this with std::shared_ptr or std::unique_ptr ? I could not find a solution without requiring the source pointer to be passed to LegacyAllocator.

+6
source share
3 answers

Yes, you can use custom deleter with std::unique_ptr or std::shared_ptr , for example:

 struct Deleter { void operator()(LegacyObj *p) const { LegacyDeleter(&p); } }; std::unique_ptr<LegacyObj, Deleter> MakeLegacyObj() { LegacyObj *p = 0; LegacyAllocator(&p); return std::unique_ptr<LegacyObj, Deleter>(p); } std::unique_ptr<LegacyObj, Deleter> p = MakeLegacyObj(); 

And, as @Dave correctly pointed out, this works with shared_ptr :

 std::shared_ptr<LegacyObj> p = MakeLegacyObj(); 
+3
source

You can use unique_ptr to remove memory, but you need to provide your own Deleter class, because memory is allocated using malloc , not new .

Even better, change the distribution code to use new and just use unique_ptr . If you take this road, you can simply return the unique_ptr instead of a pointer to memory.

Assuming you need to provide your own custom debugger, here is one way you can do this:

 template <typename T> class MallocDeleter { public: void operator() (T* obj) const { LegacyDeleter (*obj); } }; typedef std::unique_ptr <LegacyObj, MallocDeleter <LegacyObj>> unique_legacy_ptr; 

Perhaps you can also provide a function of type make_unique_legacy that allocates with a call to LegacyAllocator , rather than initializing unique_ptr .

0
source

You can create a factory function for unique_ptr as follows:

 typedef void(* LegacyDeleterType)(LegacyObj*); typedef std::unique_ptr<LegacyObj,LegacyDeleterType> UniqueLegacyPtr; UniqueLegacyPtr makeUniqueLegacyObj() { LegacyObj * p = nullptr; LegacyAllocator( &p ); return UniqueLegacyPtr( p, [](LegacyObj*p){ LegacyDeleter(&p); } ); } 

Now you can use this to create unique_ptr , and you can also assign shared_ptr , which automatically write a custom deaerator when building:

 int main() { auto unique = makeUniqueLegacyObj(); std::shared_ptr<LegacyObj> shared = makeUniqueLegacyObj(); } 
0
source

All Articles