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.
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 thetemplate<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).
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);