Search code examples
c++c++11raii

RAII using custom deleter of unique_ptr


I want to do automatic cleanup using std::unique_ptr but to initialize the unique_ptr I need an address to point to. How can I avoid the noUse variable?

bool noUse;
auto deleter = [](bool *){ DESTROY_SOMETHING }; 
std::unique_ptr<bool, decltype(deleter)> upp(&noUse, deleter); // I want to avoid the usage of this extra noUse variable
START_SOMETHING

//COMPLEX IF ELSE RETURN LOGIC

**UPDATE: ** I finally resorted to this code. Thanks everyone for quick responses.

auto deleter = [](void *){ DESTROY_SOMETHING };
std::unique_ptr<void, decltype(deleter)> upp(nullptr, deleter);

if(enabled)
{
   //START_SOMETHING
   upp.reset(reinterpret_cast<void *>&upp);
}

//COMPLEX IF ELSE RETURN LOGIC


Solution

  • As long as the custom deleter doesn't require noUse to have any specific value, you can use some arbitrary integer literal (other than zero, that would make it a null pointer):

    auto deleter = [](bool *){ DESTROY_SOMETHING };
    
    // slightly longer but more robust:          reinterpret_cast<bool*>(&upp)
    std::unique_ptr<bool, decltype(deleter)> upp(reinterpret_cast<bool*>(1), deleter);
    // ...
    

    This is a bit of a hack, and there are better solutions, such as std::experimental::scope_exit. It also doesn't make much sense that this is using a bool*, why not just a void* if we don't care about the type?

    You can also build your own solution, see:

    @Joe has written a solution very similar to std::experimental::scope_exit which does this.


    Note 1: conversion of 1 to bool* has implementation-defined effect ([expr.reinterpret_cast]), using this pointer is implementation-defined ([basic.stc.general]), and comparison of the pointer to nullptr is implementation-defined, though never UB ([expr.eq]).

    Note 2: Developers commonly create a local static variable and take its address when they need a std::unique_ptr as a "scope exit object". This is the most robust option.