3

I simply want to use the deleter feature of a shared_ptr without using the shared_ptr part. As in, I want to call a function when the shared_ptr goes out of scope and the deleter doesn't need any pointer passed to it.

I have this but it's cheezy.

shared_ptr<int> x(new int, [&](int *me) { delete me; CloseResource(); }); 

Is there a way to not associate any pointer with the shared_ptr?

Update: as per many suggestions, the unique_ptr way would look like this:

unique_ptr<int, std::function<void(int*)>> x(new int, [&](int *me) {delete me; CloseResource();});

Which frankly looks worse than the shared_ptr version, as much 'better' as it is.

Update: for those who like using this simple scope closing function caller I made it a bit simpler, you don't even need to allocate or free an object:

shared_ptr<int> x(NULL, [&](int *) { CloseResource(); });
stu
  • 8,461
  • 18
  • 74
  • 112
  • 1
    `CloseResource` should accept a pointer to the resource it is closing – Slava Feb 22 '17 at 17:09
  • 5
    Why do you need a shared pointer. Why not just rely on the object itself going out of scope? – Mad Physicist Feb 22 '17 at 17:09
  • 1
    First, using `unique_ptr` would be more efficient (assuming you aren't using the shared-lifetime aspect of `shared_ptr`). Second, if you can use Boost, there's Boost.ScopeExit which accomplishes this. Third, if you can't use Boost, [you can easily make your own class that does this](http://stackoverflow.com/questions/3669833/c11-scope-exit-guard-a-good-idea) to avoid the hacky use of `unique_ptr`/`shared_ptr`. – Cornstalks Feb 22 '17 at 17:11
  • 4
    What do you mean 'without shared_ptr part' Are you interested in multiple clients using the shared resource or not? Because if not, this is a terrible idea since shared_ptr is actually quite complex, involves atomic manipulations, dynamic block allocation and more that is not a free meal. – Adrian Lis Feb 22 '17 at 17:12
  • @Cornstalks OP may need to share onwership of the resource – Slava Feb 22 '17 at 17:12
  • Could you give an [mcve]? there is too much unknowns. – YSC Feb 22 '17 at 17:13
  • 1
    Also such exploiting makes your head hurt in about 3 weeks from now when you come back to the code and scratch your head. shared_ptr implies something and the implication might be very strong, misusing it like this sounds like asking for trouble. – Adrian Lis Feb 22 '17 at 17:14
  • @MadPhysicist The object itself is a c struct, allocated by a library, and I need to call the library's resource free-er function. – stu Feb 22 '17 at 17:21
  • @Cornstalks, not using boost, just stl. Unique_ptr or shared_ptr, same thing, I'm wondering about the usage of it, doesn't matter which one I use. – stu Feb 22 '17 at 17:22
  • @AdrianLis as above I don't want to reimplement something that already exists. Something that goes out of scope and calls an arbitrary function when it does so. It may be an unintended use, but it's a known quantity, well tested and implemented everywhere. Why author it again? – stu Feb 22 '17 at 17:23
  • 1
    @stu because its not designed to be a scope guard... Its for resource management But w/e – Adrian Lis Feb 22 '17 at 17:25
  • I'm trying to make a scope guard. shared_ptr has the functionality I want. So I thought I'd use it, even if it that was not its intended purpose. Cars can do a lot more than drive from point A to B, but that's all most people use them for. Some use them for homes. Not the intended purpose, but it works. – stu Feb 22 '17 at 17:27
  • 3
    @stu if you wish to use it like so then nobody is stopping you, just pointing out this is plain stupid. If you really want just the scope guard unique_ptr would be better since it doesnt allocate memory dynamically for the control block and doesnt incur atomic increments for the resource ref counts. – Adrian Lis Feb 22 '17 at 17:32
  • unique_ptr it is. thanks. – stu Feb 22 '17 at 17:43
  • 1
    "I need to call the library's resource free-er function." There's a word for an object that does that: a scope guard. So you _are_ trying to make a scope guard. – Mooing Duck Feb 22 '17 at 18:00
  • 1
    @Dan I have been RAIIing my objects since 1997 or so when I first discovered the wonders of destructors, and since then I've made a bazillion *cleaner classes each one does nothing but have a destructor that frees some resource. Then templates came, and I was able to make a few templates that covered most cases I needed, but as the question referred to shows, the best solution is still to make a custom class for everything, and in 2017 that just seems bizarre to me. – stu Feb 22 '17 at 18:01
  • @stu: That's why almost 18 years ago we made [Scope_guard](http://stackoverflow.com/a/31365171/845092) and unique_ptr. – Mooing Duck Feb 22 '17 at 18:02
  • 1
    @MooingDuck Then why, why oh why, do things like scope guard not make it into the c++ standard but ever more complex and unnecessary things do? C++ is supposed to be a general purpose language, presumably used to solve real world problems. Something got lost along the way. /rant. – stu Feb 22 '17 at 18:44
  • @stu: Honestly, I'm not sure why `Scope_guard` was never standardized. The committee probably just never saw the need. `Scope_guard` has some awkward overhead with dynamic allocations, wheras a custom one is free and only ~4 lines of code. – Mooing Duck Feb 22 '17 at 19:30

2 Answers2

3

It sounds like what you may be trying to do is turn over "delete" responsibilities to "somebody else" so you no longer have to worry about it. If that's the case, unique_ptr (not shared_ptr) with a custom deleter will work:

struct Foo final {};
Foo* New() { return new Foo; }
void Delete(Foo* p) { delete p; }

int main()
{
    auto p = New();
    std::unique_ptr<Foo, decltype(&Delete)> up(p, &Delete);
}

There are a number of similar solutions listed here; there's not much more that can be done without more information about your actual API (e.g., does the HANDLE is really a pointer trick work?). And even more reading at The simplest and neatest c++11 ScopeGuard, including a link to a proposed std::unique_resource.

Community
  • 1
  • 1
Ðаn
  • 10,934
  • 11
  • 59
  • 95
0

If you want a RAII like semantics with a lambda maybe something like this will do the job?

namespace details {
  enum class ScopeExitDummy { };
}


template<typename T>
class scope_exit_t
{
public:
  inline scope_exit_t(T&& codeChunk_)
    : m_codeChunk(std::forward<T>(codeChunk_)) {
  }

  inline scope_exit_t(scope_exit_t<T>&& rhs_)
    : m_codeChunk(std::move(rhs_.m_codeChunk))
  {
  }

  ~scope_exit_t() {
    m_codeChunk();
  }

private:
  T m_codeChunk;
};

template<typename T>
inline scope_exit_t<T> operator+(details::ScopeExitDummy, T&& functor_) {
  return scope_exit_t<T>{std::forward<T>(functor_)};
}

#define AtScopeExit auto TW_UNIQUE_VARIABLE(_scopeExit) = details::ScopeExitDummy{} + [&]

And usage is like:

AtScopeExit {
  CloseResource();
};

Note: TW_UNIQUE_VARIABLE is just a macro that generares a unique variable name so that the code does not clash with hand-written declarations.

#define TW_STR_CONCAT_IMPL(x, y) x##y
#define TW_STR_CONCAT(x, y) TW_STR_CONCAT_IMPL(x, y)

#ifdef __COUNTER__
  #define TW_UNIQUE_VARIABLE(prefix) TW_STR_CONCAT(prefix, __COUNTER__)
#else
  #define TW_UNIQUE_VARIABLE(prefix) TW_STR_CONCAT(prefix, __LINE__)
#endif
Adrian Lis
  • 647
  • 4
  • 20
  • Yeah, I could make a templated class, but I was trying to avoid that since all the functionality I need exists in the shared_ptr already. Why reproduce the wheel? – stu Feb 22 '17 at 17:20
  • 4
    Shared pointer is not designed to just call a deleter on destruction - it is much more. Its not reinventing a wheel its trying you to stop using that wheel to make a spaghetti – Adrian Lis Feb 22 '17 at 17:21
  • Computers are much more but we only use them to play games and make spreadsheets. Doesn't everybody like reuse? I'm just reusing part of existing functionality, and not all of it. I'm really surprised there isn't an stl solution to this real world problem that doesn't involve doing bad things like this. – stu Feb 22 '17 at 17:25
  • I'm getting the idea, that the answer is "no, there is no stl way to do this without writing your own scope guard." – stu Feb 22 '17 at 17:29
  • 2
    @stu: The answer is actually "It's easy to use `shared_ptr`/`unique_ptr` to just call a function at scope exit, but doing so is somewhere between a hack and a kludge, and it's better to just create a custom class that's properly designed for the task." It's a bit like using a screw driver as a hammer... it works (and I've done it before) and makes sense if you're in a pinch, but it's better to use a proper hammer, especially when it's so easy to reach for. – Cornstalks Feb 22 '17 at 17:38
  • @Cornstalks I agree for correct and right and good and all that, I see now the proper way is to write my own template class that does what I want. I am just aghast that after years of stl, there is no built in way to do something this simple, when raii is all the rage. – stu Feb 22 '17 at 17:55
  • 1
    @stu: That's because everyone write their own scope guard. It's like 4 lines of code. It's actually easier to write a custom scope guard than to use `shared_ptr` for this. – Mooing Duck Feb 22 '17 at 18:04
  • @MooingDuck I disagree, I can do the shared/unique_ptr thing in one line. – stu Feb 22 '17 at 18:08
  • True. But requires an include. `struct ResourceCloser{~ResourceCloser(){CloseResource();}}guard;` doesn't. – Mooing Duck Feb 22 '17 at 18:12
  • @stu: Add to that one line and the `#include` a long comment in which you explain the special purpose of your `std::shared_ptr`-related class to future readers of your code, who will otherwise have trouble understanding what's going on. – Christian Hackl Feb 22 '17 at 19:41