16

Currently I'm using some functions from the glib library. With glib also comes the gio. glib is a C library and therefore I need to delete some structures that I create.

for many of the objects I create a smartpointer eg:

std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref);

For this creates a shared pointer to an GAsyncQueue and this is safely destroys the queue on its end of its life.

However, I encounter a problem when I obtain a pointer from the gio library that I should not free. In the following code my_connection is a GSocketClient which implements (in glib speak) GIOStream.

std::shared_ptr<GInputStream> my_input_stream = 
     std::shared_ptr<GInputStream> (
        g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()))
     );

Because the documentation on GIOStream mentions, that the pointer obtained with g_io_stream_get_input_stream() should not be freed. That is because it is owned by the my_connection instance. I thought about creating a lamda for the destroy object, the second parameter of a shared pointer object. eg auto deleter = [](GInputStream* ptr) {}; and then give that lambda as destroy function to the shared pointer, but that feels a kind of stupid.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
hetepeperfan
  • 4,292
  • 1
  • 29
  • 47
  • 3
    Why use a (smart) pointer at all? Wouldn't a reference be enough? – edmz Dec 06 '15 at 19:07
  • @black I'm still a little pondering about it. The input stream is an instance of an object thats gets copied. The GIOStream gets destroyed when the last copy destructor is called. Perhaps since I don't need to destroy it anyway, is a plain old pointer also alright... – hetepeperfan Dec 06 '15 at 19:10
  • And a pointer I can set to NULL, then it is easier to see whether it is initialized. – hetepeperfan Dec 06 '15 at 19:28

3 Answers3

12

Well, alternative to no-op deleter might be using aliasing shared pointer

template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

It shares x, but after get() you'll get back p.

Discussion: What is shared_ptr's aliasing constructor for?

Community
  • 1
  • 1
Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
6

You probably just don't need a std::shared_ptr. And you probably don't even need a pointer.

As I read your question and comments, I don't see any point against

auto& my_input_stream = *( g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) )

It is true that pointers allow optional data. However, it's also true that it's mostly used the wrong way. Having

void foo( type* ptr)
{
    if (!ptr)
        throw exception;
}

often doesn't make sense. If the function has to to work on concrete data, allowing a NULL parameter is only useful if you then worry about providing that data. Otherwise, just require a reference (possibly const) to the object.

Smart pointers are useful; but they're still pointers. Avoiding them altogether, if possible, is even better.


From the comments:

However, a reference must always be initialized

Absolutely. Since C++11 though we've got std::reference_wrapper which can also be reassinged and stored in containers.

edmz
  • 8,220
  • 2
  • 26
  • 45
  • Based on my question you are right. However, a reference must always be initialized. Since my_input_stream is a class member(that didn't show in my question) that is not opened. I can set it only when I actually open the socket connection. But I'll upvote it any since it is a good answer for other readers. – hetepeperfan Dec 06 '15 at 20:09
  • @hetepeperfan True. See if my update can address that ;) – edmz Dec 06 '15 at 20:16
5

You can use a deleter type that does nothing, but it will need to be passed as an argument to the shared_ptr's constructor

struct DoNothing {
    template <typename T>
    void operator()(T*) const noexcept { }
};

When creating a shared_ptr you will need to create one of these deleters and pass it in the constructor (as you're doing with the lambda). You can make this easier on yourself with an intermediate function

template <typename T>
std::shared_ptr<T> non_deleting_shared_ptr(T* ptr) {
    return {ptr, DoNothing};
}

auto my_input_stream = 
    non_deleting_shared_ptr(
        g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()));

However the bigger question is why you're using smart pointers when you don't want ownership to be a part of it. You'd almost certainly be better off with just a GAsyncQueue*, unless of course you're in a situation where you have a shared_ptr that needs to free sometimes. Like a data member maybe?

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174