3

I'm trying to wrap an object from the C-API of OpenCV (CvPOSITObject) in a smart-pointer. To my understanding it should be something like the following:

unique_ptr<CvPOSITObject, decltype(cvReleasePOSITObject)> positObject;
positObject = unique_ptr<CvPOSITObject, decltype(cvReleasePOSITObject)>(cvCreatePOSITObject(param1, param2), cvReleasePOSITObject);

But I get a compiler error, and google didn't really help.

The declarations of the two functions are:

CVAPI(CvPOSITObject*)  cvCreatePOSITObject( CvPoint3D32f* points, int point_count );
CVAPI(void)  cvReleasePOSITObject( CvPOSITObject**  posit_object );

I get something like

1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1227): error C2207: 'std::_Unique_ptr_base<_Ty,_Dx,_Empty_deleter>::_Mydel' : a member of a class template cannot acquire a function type
1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1322): warning C4180: qualifier applied to function type has no meaning; ignored
1>  Myfile.cpp
1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1221): warning C4180: qualifier applied to function type has no meaning; ignored
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1283) : see reference to class template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,_Empty_deleter>' being compiled
1>          with
1>          [
1>              _Ty=CvPOSITObject,
1>              _Dx=void (CvPOSITObject **),
1>              _Empty_deleter=false
1>          ]
1>          C:\MyDir\Myfile.hpp(71) : see reference to class template instantiation 'std::unique_ptr<_Ty,_Dx>' being compiled
1>          with
1>          [
1>              _Ty=CvPOSITObject,
1>              _Dx=void (CvPOSITObject **)
1>          ]

How do I do that correctly?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Ela782
  • 5,041
  • 5
  • 53
  • 66

2 Answers2

7

There are two problems with your code. The first, as BenVoigt mentions in his answer, is that decltype will not trigger the implicit conversion from function type to pointer to function type, so you must take the address of the function explicitly. Your code changes to

positObject = unique_ptr<CvPOSITObject, 
                         decltype(&cvReleasePOSITObject)>(
                         cvCreatePOSITObject(param1, param2), 
                         cvReleasePOSITObject);

However, this will now fail to compile for a different reason, which brings us to the second problem. cvReleasePOSITObject takes an argument of type CvPOSITObject ** but the unique_ptr above will attempt to call its deleter with CvPOSITObject *. To fix this, just use a lambda expression for the deleter.

positObject = unique_ptr<CvPOSITObject, 
                         void(*)(CvPOSITObject *)>(
                         cvCreatePOSITObject(param1, param2), 
                         [](CvPOSITObject *p) { cvReleasePOSITObject(&p); });

If you want to separate the declaration and initialization of the unique_ptr there are a couple of options. The first example demonstrates how to use a lamda expression for this.

auto deleter = [](int *p) {
    delete p;
};

int main()
{
    std::unique_ptr<int, decltype(deleter)> p(nullptr, deleter);    
    p.reset(new int(42));
}

You'll still have to pass the deleter instance to the unique_ptr, otherwise it'll attempt to default construct the deleter, which fails because closures generated from lambda expressions have deleted default constructors (§5.1.2/20 from N3691).

The other option is to write the deleter as a functor, which allows default construction.

struct deleter
{
    void operator()(int *p) const
    {
        delete p;
    }
};

int main()
{
    std::unique_ptr<int, deleter> p;

    p.reset(new int(42));
}
Community
  • 1
  • 1
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Compiles and looks like it does exactly what it should do. Thanks! Especially for the second part, which was very helpful! – Ela782 Oct 03 '13 at 23:07
  • Is there any way to split the variable declaration and the actual initialization? If I put all on one line, it works, if I split it I get an error "unique_ptr constructed with null deleter pointer". – Ela782 Oct 04 '13 at 09:00
3

Try

decltype(&cvReleasePOSITObject)

There's an implicit conversion from the name of a function to a pointer to that function, but function types and function pointer types are not the same, and decltype doesn't cause implicit conversions.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720