0

I'm well aware of the RAII pattern and std::unique_ptr and other "smart pointers" in C++11, but there's still some cases which I can't figure out how to handle nicely without having a goto Cleanup section which does the cleanup finally.

Specifically, I'm thinking about Windows programming where sometimes i'll want to leak handles and other times I won't.

If I have a function that looks kind of like he following:

PROCESS_INFORMATION p;
if (!CreateProcess(... &p))
{
  throw windows_error(GetLastError());
}

DWORD r = WaitForSingleObject(h, 5000);
if (r == WAIT_TIMEOUT || r == WAIT_FAILED)
{
  // terminate process, close handles
  throw ...;
}

if (!SetEnvironmentVariable(...))
{
  // terminate process, close handles
  throw windows_error;
}

(a few other operations that if they fail i have cleanup to do).

return process handle;

I don't really see how unique_ptr can help me here, unless i use release() on the unique_ptr after all the if to indicate success/tell the unique_ptr to not clean it up. (I can create a special deleter for unique_ptr so it cleans up windows handles properly). However, my question is that is such a usage of release() on smart pointers "correct" for cases where i want to leak allocated memory/handles back to the caller? Is there a better way to do it? Returning a shared_ptr can work too, I suppose...

atanamir
  • 4,833
  • 3
  • 24
  • 20
  • When you say “leak to the caller”, are you talking about the return value of the function? – svick Apr 19 '14 at 19:34
  • In my particular case right now, i'm saving it to a private HANDLE field in my class. But yes, in general, i juts mean either to the caller, or "lifetime past the scope of the function". – atanamir Apr 19 '14 at 19:52
  • The description under the code sounds very much like the famous Scope Guard (Alexandrescu/Marginean). [Here's the original article](http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758) but there are better implementations available for C++11. However, it might be more appropriate to build/use a class with the sole responsibility of managing that Process resource. – dyp Apr 19 '14 at 21:16

1 Answers1

0

When you want to pass a resource from your function, then there are two cases: either the resource is copyable (it has accessible copy constructor) or not.

If the resource is copyable (e.g. shared_ptr), then you can simply copy it to wherever it needs to go and you're done. When your function returns, it's only your copy of the resource that's destroyed.

If the resource is not copyable (e.g. unique_ptr), then you need to move it, which is what the move semantics in C++ 11 is all about.

When you move the resource to a new location, the old location becomes empty, so when its destructor is called, there is nothing to do.

If you're passing the resource using return, then you don't need to do anything special, return moves automatically if it can.

For example:

std::unique_ptr<resource> get_resource()
{
    std::unique_ptr<resource> result(new resource());

    if (result->is_something_wrong())
    {
        throw std::exception();
    }

    return result;
}

If you want to pass the resource to a field, or something like that, then you need to explicitly say that you want to move it by using std::move:

class resource_user
{
    void init_resource()
    {
        std::unique_ptr<resource> result(new resource());

        if (result->is_something_wrong())
        {
            throw std::exception();
        }

        resource_ = std::move(result);
    }

    std::unique_ptr<resource> resource_;
};
Community
  • 1
  • 1
svick
  • 236,525
  • 50
  • 385
  • 514
  • Move should be the default, since it typically is faster than copying and has a different meaning (the original resource is reused). Also, there's a third case for resources that are neither copyable nor movable, such as `std::mutex` (but those are much less common). – dyp Apr 21 '14 at 11:57
  • @dyp I don't think it should be the default. When I do `a = b`, I wouldn't expect `b` to become empty. – svick Apr 21 '14 at 12:02
  • 1
    That's not what I meant. OP has a function that acquires a resource and wants to get the resource out there (return it). This should be done via moving per default. – dyp Apr 21 '14 at 12:03