1

I am working with shared_ptr storing pointers of a C library. Here an example of such a C library containing the header bar.h:

#pragma once

typedef struct Flupp MyFlupp;

MyFlupp *
create_flupp();

void 
del_flupp(MyFlupp * fp);

void
print_flupp(MyFlupp * f);

Here the struct has a forward declaration and is defined in the bar.so. I am using the bar.so in my C++ code:

#include <memory>

extern "C"{
#include "bar.h"
}

int main()
{
    std::shared_ptr<MyFlupp> flupp_ptr(nullptr, del_flupp);
    flupp_ptr.reset(create_flupp());

    print_flupp(flupp_ptr.get());
    return 0;
}

Here I am storing the MyFlupp* in a shared_ptr. On the declaration, MyFlupp* is unknown and set to nullptr. Later I am calling the reset operation to set the valid pointer. But when I am compling the code, I get the following error:

In file included from /usr/include/c++/8/bits/shared_ptr.h:52,
                 from /usr/include/c++/8/memory:81,
                 from test_foo.cpp:1:
/usr/include/c++/8/bits/shared_ptr_base.h: In instantiation of ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*) [with _Yp = Flupp; <template-parameter-2-2> = void; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’:
/usr/include/c++/8/bits/shared_ptr_base.h:1293:4:   required from ‘std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> std::__shared_ptr<_Tp, _Lp>::reset(_Yp*) [with _Yp = Flupp; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2; std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> = void]’
test_foo.cpp:10:35:   required from here
/usr/include/c++/8/bits/shared_ptr_base.h:1126:19: error: invalid application of ‘sizeof’ to incomplete type ‘Flupp’
    static_assert( sizeof(_Yp) > 0, "incomplete type" );

When I am providing the deleter to the reset operation than it is working.

flupp_ptr.reset(create_flupp(), del_flupp);

Can anybody explain me whats going on? I already looked @cppreference but I does not found an answer.

  • If the compiler did not know what it should delete, what do you think it could generate code for? delete calls the destructor and free the memory. Which destructor it should call if the type is unknown. – Klaus Jan 04 '20 at 18:06
  • `flupp_ptr.reset(create_flupp());` doesn't mean "reset the pointer but keep the current deleter", as you seem to expect. It means "reset the pointer, and reset the deleter to the default one that would call `delete` on that pointer". – Igor Tandetnik Jan 04 '20 at 18:30
  • Klaus thats why I am providing the deleter at construction time. Igor yes i think you are right but i don't know why that behaviour is not documented. – Herold Christian Jan 05 '20 at 08:04

1 Answers1

0

The problem is that the type Flupp has only been forward-declared, but not defined. In the context of the use here, it is considered an incomplete type.

This has certain implications for the use with std::shared_ptr:

std::shared_ptr may be used with an incomplete type T. However, the constructor from a raw pointer (template<class Y> shared_ptr(Y*)) and the template<class Y> void reset(Y*) member function may only be called with a pointer to a complete type (note that std::unique_ptr may be constructed from a raw pointer to an incomplete type).

Source: cppreference.com

Instead you need to use the respective overloads that accept a pointer and the deleter as arguments.

With unique_ptr this is not necessary, as that one stores the custom deleter as part of the type. But with shared_ptr the deleter is type-erased and only recovered at runtime. This allows you to change the deleter of an existing shared_ptr when calling reset. For this reason you always need to re-state which deleter to use whenever you're calling reset. If no deleter is given, each call to reset will also implicitly reset the deleter to just calling delete on the managed pointer.

So to make it work, just change your reset call to

flupp_ptr.reset(create_flupp(), del_flupp);
ComicSansMS
  • 43,180
  • 11
  • 123
  • 140