Home > Net >  What is the use of a custom unique_ptr deleter that call delete?
What is the use of a custom unique_ptr deleter that call delete?

Time:05-19

In the C samples provided by NVidia's TensorRT library, there is a file named common.h that contains definitions of structures used throughout the examples.

Among other things, the file contains the following definitions:

struct InferDeleter
{
    template <typename T>
    void operator()(T* obj) const
    {
        delete obj;
    }
};

template <typename T>
using SampleUniquePtr = std::unique_ptr<T, InferDeleter>;

The SampleUniquePtr alias is used throughout the code samples to wrap various pointers to interface classes returned by some functions, e.g. SampleUniquePtr<INetworkDefinition>(builder->createNetworkV2(0));

My question is, in what practical aspects are std::unique_ptr and SampleUniquePtr different? The behavior of SampleUniquePtr is pretty much what I would expect from std::unique_ptr, at least now. Could it be for compatibility with old versions of C ?

CodePudding user response:

From the history I see it was for a while

template <typename T>
void InferDeleter::operator()(T* obj) const
{
    if (obj)
    {
        obj->destroy();
    }
}

Then they declared destroy() methods deprecated:

  • Destructors for classes with destroy() methods were previously protected. They are now public, enabling use of smart pointers for these classes. The destroy() methods are deprecated.

Although it is deprecated, it is still not removed and the old ABI InferDeleter should be kept for backward compatibility for applications linked with the old TensorRT. Thus they are using since recently

template <typename T>
void InferDeleter::operator()(T* obj) const
{
    delete obj;
}

not removing the struct InferDeleter and using SampleUniquePtr = std::unique_ptr<T, InferDeleter>. But it may be removed in the future. When they remove struct InferDeleter and change using SampleUniquePtr = std::unique_ptr<T>, it will make TensorRT library incompatible with old software.

CodePudding user response:

I don't have the time to scan through the entirety of the linked library, so there is a chance I'm wrong on this count.

But, what you're seeing is probably a default implementation that's used as a fallback for any type that doesn't need special handling.

The Library Implementor always has the option of specializing this function in the situation where they're dealing with a type that actually does need special handling, which is probably pretty common for a library made by Nvidia (and therefore probably meant to interact with the GPU).

class GPUResource {
//...
~GPUResource() noexcept {} //Does NOT perform the special handling, for whatever library-specific reason
};

template<>
void InferDeleter::operator()<GPUResource>(GPUResource * obj) {
    performSpecialCleanupOnGPUResource(obj->handle);
    delete obj;
}

In practice, this kind of stuff always smells like an anti-pattern (are you sure, Library Implementor, that you couldn't do the cleanup in the destructor of this object??) but if they had good reasons for separating out the logic like this, the way they've defined InferDeleter permits them this kind of flexibility.

CodePudding user response:

The example looks pointless as that is exactly what I expect the default deleter to do.

But maybe this is better (untested):

#include <vector>
struct VectorDeleter
{
    template <typename T>
    void operator()(std::vector<T*> *v) const
    {
        for(auto p : *v) delete p;
        delete v;
    }
};

template <typename V>
using UniquePtrVector = std::unique_ptr<V, VectorDeleter>;

When you have a vector of pointers (to e.g. polymorphic classes) you have to delete the objects the vector points to when the vector is deleted. The VectorDeleter does that before the vector itself is deleted.

  • Related