Search code examples
c++unique-ptrc++pmr

Using a custom delete functor (that accesses member) for a member std::unique_ptr


I have a class entity that uses a stateful polymorphic allocator and std::unique_ptr to manage a resource. Naturally, I have to provide a custom deleter for the unique_ptr that pairs with allocator_.new_object() that I pass in the constructor. However, as this deleter must use the same allocator again, I need to access the allocator member from within the deleter. This appears to be quite tricky. Using this answer I was able to get this far, but now gcc complains that struct entity is incomplete. Any way to get around this?

Demo

#include <string_view>
#include <cstdio>
#include <memory>
#include <memory_resource>
#include <type_traits>

struct StaticTimer_t
{
    std::string_view hello_ = "Hello World!";
};

template <typename T> requires (std::is_pointer_v<T>)
auto deleter(T e) {
    return [e](StaticTimer_t* p){ e->get_allocator().delete_object(p); };
}

struct entity
{
    using allocator_t = std::pmr::polymorphic_allocator<std::byte>;

    auto get_allocator() -> allocator_t
    {
        return allocator_;
    }

    entity(allocator_t allocator = {})
        :   allocator_( allocator ) 
        ,   ptr_( allocator_.new_object<StaticTimer_t>(), deleter(this) )
    { }

    auto print()
    {
        printf("%s\n", ptr_.get()->hello_.data());
    }

    allocator_t allocator_;
    std::unique_ptr<StaticTimer_t, decltype(deleter<entity*>(nullptr))> ptr_;
};


int main()
{
    entity e;
    e.print();
}

Error:

<source>:14:38: error: invalid use of incomplete type 'struct entity'
   14 |     return [e](StaticTimer_t* p){ e->get_allocator().delete_object(p); };
      |                                   ~~~^~~~~~~~~~~~~
<source>:17:8: note: forward declaration of 'struct entity'
   17 | struct entity
      |        ^~~~~~

Note: I'm looking for a way to get around std::function!


Solution

  • Pass the allocator directly instead of the entity. It is all you need and it is complete where you need it to be, unlike entity.

    using allocator_t = std::pmr::polymorphic_allocator<std::byte>;
    
    auto deleter(allocator_t e) {
        return [e](StaticTimer_t* p) mutable { e.delete_object(p); };
    }
    
    entity(allocator_t allocator = {})
        :   allocator_( allocator ) 
        ,   ptr_( allocator_.new_object<StaticTimer_t>(), deleter(allocator_) )
    { }
    
    std::unique_ptr<StaticTimer_t, decltype(deleter(std::declval<allocator_t>()))> ptr_;
    

    Alternatively, replace the lambda with a struct (KamilCuk's answer).