3

I implemented a C-API for a C++ class which uses shared-pointers of other objects to access them. In my C-API I can of course only get raw pointers. So I "convert" the raw pointer in my C-API to a shared-pointer and use this then with my C++ class methods:

method(std::shared_ptr<dataType>(raw-pointer));

Now I have the problem that at the end of "method" always the shared-pointer destructor is called and it unfortunately kills the object my raw-pointer is pointing at (which I don't want). So, how can I prevent the raw-pointer from being killed?

I already tried shared-pointer functions like reset() or swap(), but they all didn't let my raw-pointer go...

bool Traffic_doStep(traffic_handle t, environment_handle e, double cycletime) {
    if (!valid(t, __FUNCTION__)) return false;
    if (!valid(e, __FUNCTION__)) return false;
    if (!valid(cycletime, __FUNCTION__)) return false;

    try {
        t->doStep(std::shared_ptr<Environment>(e), cycletime);
        return true;
    }
    catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return false;
    }
}

Expected result would be that the raw-pointer e is still pointing to a valid object after this function returned. Actually the raw-pointer points then to a deleted object.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • 7
    Why are you using `std::shared_ptr` for pointers you don't own? That goes squarely against the whole point of `std::shared_ptr`... – Max Langhof Aug 14 '19 at 15:15
  • 3
    It seems you are under the impression that `shared_ptr` should substitute all uses of pointers. This is not the case. It should only be used for pointers you own (pointers you are responsible for cleaning up after). If your pointer does not own the object it points to, it should not be a `std::shared_ptr`. – François Andrieux Aug 14 '19 at 15:16
  • 2
    In this question it is also unclear what came first: The C API or the C++ class. You say you wrote a C API for a C++ class, but what you actually do is _receive_ a raw pointer _from_ a C API. So who owns the pointer? Your C++ code or some external C code? We are missing the other side of the API here. – Max Langhof Aug 14 '19 at 15:19
  • I receive this raw-pointer from the C-API of another class (Environment). There it's normally a unique-pointer, but has been made available in C by releasing it. Now I must have a C-API from my class (Traffic) which only uses the stuff available in C. That's why I must handle the raw-pointer e. – Lars Reimer Aug 14 '19 at 19:24

2 Answers2

14

Don't put the pointer in a std::shared_ptr

The purpose of Smart Pointers in C++ is to provide automatic Lifetime management. When you write std::shared_ptr<int> ptr{raw_ptr};, the expectation is that when ptr goes out of scope, the object pointed to by raw_ptr will be delete'd. If this is not the intent, then you should not be placing the pointer in a smart pointer.

So if your application does not manage the lifetime of a pointer, then it is perfectly acceptable to store a raw pointer.

If the API behind the function cannot be altered, you will instead need to construct the std::shared_ptr with a no-op deleter function, so that when clean-up is called, nothing will happen to the pointer.

try {
    std::shared_ptr<Environment> temp_ptr{e, [](int *) {}/*No-Op Deleter*/};
    t->doStep(temp_ptr, cycletime);
    return true;
}

This will solve your problem, but this is, of course, an Antipattern; don't do this unless you've been forced to by API design constraints that you cannot control.

Community
  • 1
  • 1
Xirema
  • 19,889
  • 4
  • 32
  • 68
  • Thank you! I will try this out. Alternatively I could change my C++ functions to use raw-pointers. Since my class should not be responsible for the object behind "e" and it's not possible that this object vanishs while my functions are executed, this might be a way to go. I just was told to better use smart-pointers, so I left this option out for now. – Lars Reimer Aug 14 '19 at 19:32
  • @LarsReimer I would say somewhere around 80-90% of the time, it's correct to only use smart pointers. But the kind of scenario you're working with here, where you're passing around pointers to objects you don't own, and whose lifetime is at least guaranteed through the execution of a given function, is where you don't need them. – Xirema Aug 14 '19 at 19:39
2

You may provide a custom deleter of a shared pointer.

Here is a viable constructor:

template< class Y, class Deleter > 
shared_ptr( Y* ptr, Deleter d );

But I would rather use a unique pointer and then release it.

Nestor
  • 687
  • 5
  • 12