4

I'm in the following scenario:

struct container {
    data* ptr;
};

void someFunc(container* receiver /* wants to be filled */) {
    auto myData = createData(); // returns shared_ptr<data>
    receiver->ptr = myData.get();
}

The function that generates that data, and the object that receives it, are part of two different libraries, which I can't modify the source code of. I have to work with these data types, there's nothing I can do about it.

So I have to implement a function that acquires some data, then passes a pointer to that data to an object. My problem is that the function that creates my data, returns a shared_ptr instance to it. The object that needs the data will only accept a raw pointer to it.

As you can see, I call get() on the shared_ptr to get the raw pointer and pass it to the receiving object. If I'm not mistaken, then a shared_ptr decrements the reference count whenever it goes out of scope. So in this case, that means that it would destroy my data as soon as the function returns, since the reference count would reach 0.

So how can I get rid of the shared_ptr instance without destroying the managed object? The object which I pass the data to (illustrated with the "container" struct for simplicity) does take care of the memory cleaning inside its destructor, so I don't need any reference counting or anything like that. I don't want anything to watch over that allocated data anymore (except the object that receives a pointer to it). I want to get rid of the shared_ptr, and only have a raw pointer to the allocated data, which I can pass to the receiving object.

Is that possible?

notadam
  • 2,754
  • 2
  • 19
  • 35
  • Is there any particular reason you can't make the container accept a shared pointer? The whole point of them is that you don't need to worry about lifecycle management. Don't use raw pointers if you don't need to. – aruisdante Mar 08 '15 at 02:50
  • 1
    possible duplicate of [How to release pointer from boost::shared\_ptr?](http://stackoverflow.com/questions/1525764/how-to-release-pointer-from-boostshared-ptr) – aruisdante Mar 08 '15 at 02:52
  • @aruisdante I know, I use smart pointers in all my projects whenever possible. But in this case, both the `createData` and the `container` are a part of some libraries, which I can't modify (two different ones, that is. that's why they don't speak the same "language"). – notadam Mar 08 '15 at 02:52
  • 1
    Make a copy of the data. You have two things that each need control over the lifetime of the data, that requires two copies. – David Schwartz Mar 08 '15 at 02:59
  • @DavidSchwartz while it won't be the most efficient, it's probably the most readable, maintainable, and safe way to do it. – aruisdante Mar 08 '15 at 02:59
  • Actually we're talking about textures here as the data, since this is a part of a game. Textures can be quite large, so I want to avoid keeping around two copies of them. – notadam Mar 08 '15 at 03:01
  • I can't. It's a part of a library which I can't modify. – notadam Mar 08 '15 at 03:02
  • @DavidSchwartz Copying the pointer? Yes, that's cheap, but that's not the issue here. If the smart pointer goes out of scope, the data that that is points to gets deallocated. So I'm left with a pointer that points to nothing, or junk data. So I need to keep the smart pointer alive while I wish to use the raw pointer. Check the accepted answer, it provides a good solution for this! – notadam Mar 08 '15 at 23:48

4 Answers4

7
static std::map m<data *, std::shared_ptr<data> >;

struct container {
    data* ptr;
};

void someFunc(container* receiver /* wants to be filled */) {
    auto myData = createData(); // returns shared_ptr<data>, I can't do anything about it
    receiver->ptr = myData.get();
    m[receiver->ptr] = myData;
}


void someOtherFunc(container* receiver)
{
  // delete receiver->ptr;
  m.erase(receiver->ptr);
}

This elongates the life of shared_ptr through a map.

Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
  • 1
    Actually I'm integrating [Spine](http://esotericsoftware.com) into my game. It does have runtimes that support major game engines, but I use an engine that's not officially supported. For these scenarios, Spine has a "generic" runtime, that's in ANSI C, so you can make it work with any engine. To integrate it into a project, I have to implement two functions (the declarations are provided) which create and destroy textures. Spine passes in a struct* that wants a texture pointer in it (so it can pass me back those later for rendering), but my engine gives me textures I load in `shared_ptr` form – notadam Mar 08 '15 at 03:23
3

The only way I can see out of this (if your design will allow) is to wrap your container:

struct container {
    data* ptr;
};

struct container_wrapper {
    std::shared_ptr<data> ptr; // ensure this lives as long as needed
    container cnt;
};

void someFunc(container_wrapper& receiver /* wants to be filled */) {
    receiver.ptr = createData();
    receiver.cnt.ptr = receiver.ptr.get();
}
Galik
  • 47,303
  • 4
  • 80
  • 117
2

std::shared_ptr doesn't give up the ownership so you have to keep it somewhere in your class to make sure the pointer doesn't get deleted. Still, you could use the acquired shared_ptr instead of storing raw pointer. Raw pointers almost always is a bad choice.

ixSci
  • 13,100
  • 5
  • 45
  • 79
  • I can't modify the source code of either the data source function or the receiver class. They come from two different libraries, which I can't access the source code of. So I need to figure out how to make them work together. – notadam Mar 08 '15 at 02:54
2

It's not possible.

Even if you take care of object destruction in container destructor, you can't guarantee that no other object have std::shared_ptr holding pointer to that object (in this case you will see double free error).

If object lifetime is managed by std::shared_ptr (without special deleter) then you can control object lifetime only through std::shared_ptr and std::weak_ptr.

rutsky
  • 3,900
  • 2
  • 31
  • 30
  • It's not impossible (see the linked duplicate) but it's definitely neither trivial nor a particularly good idea. – aruisdante Mar 08 '15 at 02:57
  • @aruisdante Thanks for the comment. Yes technically you are correct, but I mentioned that it's not possible *without special deleter* a bit at the of an answer. – rutsky Mar 08 '15 at 02:59