6

I have some legacy-era code at work that takes in a double-pointer and allocates memory to it. A shortened example of it 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 LegacyAllocator function is ~100 lines and mixes reading from files with creating a linked list of LegacyObj pointers, and isn't something I'd be able to get away with rewriting right now. I would like, however, to make the use of this function a bit safer, avoiding any memory leaks that may occur from exceptions &tc. The first solution I came up with was to wrap it up in a class and handle calling the legacy functions in the 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 using std::shared_ptr or std::unique_ptr? I've not been able to come up with a solution without having to keep the original pointer passed to LegacyAllocator around.

1
  • unique_ptr and shared_ptr don't take allocators, they only take deleters. You would allocate the pointer yourself with LegacyAllocator(&obj) and make a deleter functor for the unique_ptr/shared_ptr which calls LegacyDeleter(&p) in its operator() Commented Dec 19, 2013 at 15:18

3 Answers 3

3

Yes, you can use a 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 correctly pointed out by @Dave, this works with shared_ptr too:

std::shared_ptr<LegacyObj> p = MakeLegacyObj(); 
Sign up to request clarification or add additional context in comments.

2 Comments

and std::shared_ptr<LegacyObj> p = MakeLegacyObj(); works too
Thanks, looks like creating a custom deleter functor was what I missing.
0

You can use unique_ptr to delete the memory, but you'll have to provide a custom Deleter class since the memory is allocated using malloc rather than new.

Better yet, change the allocation code to use new instead and just use unique_ptr. If you go down this road you can just have the allocator return a unique_ptr instead of a pointer to the memory.

Assuming you need to provide your own custom deleter, here is one way you might do it:

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

You could also probably provide a make_unique_legacy type function which allocates by calling LegacyAllocator, instead of having to initialize the unique_ptr yourself.

Comments

0

You can create a factory function for unique_ptrs like this:

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); } ); } 

You can now use that to create unique_ptrs and you can also assign to shared_ptrs which capture the custom deleter automatically at construction:

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.