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

Time:12-31

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?

requires (std::is_pointer_v) auto deleter(T e) { return [e](StaticTimer_t* p){ e->get_allocator().delete_object(p); }; } struct entity { using allocator_t = std::pmr::polymorphic_allocator; auto get_allocator() -> allocator_t { return allocator_; } entity(allocator_t allocator = {}) : allocator_( allocator ) , ptr_( allocator_.new_object(), deleter(this) ) { } auto print() { printf("%s\n", ptr_.get()->hello_.data()); } allocator_t allocator_; std::unique_ptr(nullptr))> ptr_; }; int main() { entity e; e.print(); }'),l:'5',n:'0',o:'C++ source #1',t:'0')),k:48.825065274151434,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,deviceViewOpen:'1',filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!(),options:'-Wall -Os --std=c++20',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:' x86-64 gcc (trunk) (Editor #1)',t:'0')),header:(),l:'4',m:41.3677130044843,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compilerName:'x86-64 gcc 12.2',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output of x86-64 gcc (trunk) (Compiler #1)',t:'0')),k:50,l:'4',m:58.632286995515706,n:'0',o:'',s:0,t:'0')),k:51.174934725848566,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow noreferrer">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!

CodePudding user response:

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

CodePudding user response:

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

Write your own std::function that std::bind-s entity with a operator().

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

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

struct entity;

struct entityDeleter {
    entity& e;
    entityDeleter(entity& e) : e(e) {}
    void operator()(StaticTimer_t *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>(),
            entityDeleter(*this)
        ) {}
    auto print() {
        printf("%s\n", ptr_.get()->hello_.data());
    }
    allocator_t allocator_;
    std::unique_ptr<StaticTimer_t, entityDeleter> ptr_;
};

void entityDeleter::operator()(StaticTimer_t *p) {
    printf("deleter\n");
    e.get_allocator().delete_object(p);
}

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