0

I have a C++ library that I need to use which uses a build() function to return an object with new via a raw pointer. I want to use this object while using smart pointers but so far I have only found the solution to use smart_ptr.reset(build()) which works but is kind of ugly. Is there a better way of doing this or do I have to use manual memory management?

For some more context the function from the library calls return new and I am sure you need to call delete on that pointer.

How to assign the address of an existing object to a smart pointer? this is the previous answers I based calling it "ugly" on and the reason why I was asking if there was a better way.

Jundarer
  • 45
  • 6
  • 3
    *which works but is kind of ugly* -- *or do I have to use manual memory management?* -- So you're willing to give up using smart pointers because it looks (according to you) "ugly"? That's a strange trade-off. – PaulMcKenzie Jun 07 '21 at 13:00
  • 1
    Put the ugly line into a pretty function and use *that*. – Quentin Jun 07 '21 at 13:02
  • 1
    If you have a legacy function which return raw owning pointer, adding a wrapper to have clean code seems good. – Jarod42 Jun 07 '21 at 13:03
  • Sorry I worded that badly. I did not mean that I found that ugly but rather that the answers I read said that it was ugly and it should be avoided at all costs. Since the thread was 5 years old I was wondering if there is a better way now. In no way did I mean that I don't want to use smart pointers. Thanks for the other answers I shall do it with a wrapper function. – Jundarer Jun 07 '21 at 13:08
  • 2
    you should make sure who is supposed to delete the object. You or the library? Maybe you should use a custom deleter, maybe not. – 463035818_is_not_an_ai Jun 07 '21 at 13:09
  • you should link that answers, because without reading them we cannot tell if you or the answerer misunderstood something, or what is actually meant by "ugly" – 463035818_is_not_an_ai Jun 07 '21 at 13:10
  • @463035818_is_not_a_number added the thread I was reading. – Jundarer Jun 07 '21 at 13:14
  • I cannot stress enough how important is the @463035818_is_not_a_number comment about the custom deleter is – Alessandro Teruzzi Jun 07 '21 at 13:15
  • thats a different case. When you wrap a raw pointer into a shared pointer you must be certain that this pointer points to a dynamically created object and that you are supposed to delete it at some point. Both, is not the case for the Q&A you link, but seems to fit what you desribe – 463035818_is_not_an_ai Jun 07 '21 at 13:16
  • seems to fit... Can you tell some more context? What is that pointer? Are you actually sure that you should call `delete` on it? – 463035818_is_not_an_ai Jun 07 '21 at 13:17
  • Added more clarification in the question but the pointer is returned via `return new` and definitely needs to be deleted via `delete` (as from the docs). – Jundarer Jun 07 '21 at 14:28

1 Answers1

0

It depends. Consider this example:

struct example {
    ~example() { std::cout << "bye\n"; }
};

example* some_lib_function_A(){
    return new example;
}

The library returns you a raw pointer to a dynamically allocated object. That's not nice, and to avoid dealing with the raw owning pointer you can wrap some_lib_function_A into a function that returns a smart pointer that manages the object.

However, the library might also do something along the line of this (just for the sake of the argument. It should rather return a reference):

example* some_lib_function_B() {
    static example ex;
    return &ex;
} 

And in this case you cannot delete the returned pointer without running into problems.

Complete example:

#include <memory>
#include <iostream>
struct example {
    ~example() { std::cout << "bye\n"; }
};

example* some_lib_function_A(){
    return new example;
}

example* some_lib_function_B() {
    static example ex;
    return &ex;
} 

template <typename F>
std::unique_ptr<example> wrap(F f){
    std::unique_ptr<example> res;
    res.reset(f());
    return res;
}

int main() {
    wrap(some_lib_function_A);
    wrap(some_lib_function_B); // free(): invalid pointer
}

The first line in main does what you would expect, but the second line causes undefined behavior. Thats the reason the linked answer says that it is bad to write a function along the line of:

 std::unique_ptr<example> wrap_bad(example& ex) {
    std::unique_ptr<example> res;
    res.reset(&ex);
    return res;
 }

Because you cannot know if the example passed to the function was dynamically allocated or not. The function is lying about what it does, because a function taking a reference has no business in dealing with lifetime of its parameter. It can be used correctly, but it has great potential to be used wrong.

You have to read the libraries documentation and find out what the library expects you to do with the pointer. Sometimes you must call some library function clean_up(ex) for proper clean up, in which case you can wrap the library clean up function in a custom deleter.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185