News for hackers. Stuff that matters.
Re:Why RAII rocks by mkale on Thursday April 23, 2015 @03:10PM (#9429096)
In addition, RAII is useful for more than just memory [de]allocation: database connections, resource handles, anything that you have to get/create and then cleanup/release.
Re:Why RAII rocks by czinck on Thursday April 23, 2015 @05:27PM (#9429959)
As someone that works 90% with python and 10% with C++, that's something that gets missed in the debates about GC/manual memory management. It's a lot easier to leak resources in python programs because the whole point is you aren't sure what the lifetime is of some objects; if you knew, you wouldn't need a GC. So I end up putting a with block pretty high in the call stack holding the DB handles (most of the time) which is just a weaker form of RAII, as you can't "allocate" anything further down the call stack.

I prefer working in python to C++, I just wish I had a lot more control over things like this. And no, __del__ isn't a good solution as that opens a whole can of worms (for one, it's not guaranteed to ever be called).

Re:Why RAII rocks by falcolas on Thursday April 23, 2015 @06:52PM (#9430419)
What do you mean when you say 'as you can't "allocate" anything further down the call stack'? Do you mean you can't allocate something in c to be cleaned up in 'a', in the call tree:

    a(b(c()))
because just returning an object, then dropping the reference at the end of 'a' would do just that. You could also write your own context manager to wrap up a parameter.

    class Shadow(object):
        def __init__(self):
            self.value = None
        def __enter__(self):
            return self
        def set(self, v):
            self.value = v
        def __exit__(self, *args):
            if self.value is not None:
                self.value.cleanup()
        
    with Newdb() as db, Shadow() as d:
        a(b(c(db, d)))
Re:Why RAII rocks by czinck on Friday April 24, 2015 @08:49AM (#9432944)
you can do something like

    with a(b(c)) as f:
but then you risk any of those functions throwing an exception and screwing up the whole thing. You can move the resource allocation from __init__ to __enter__, but now you split your initialization code over 2 constructors. You can just hope and pray that a and b don't throw an exception, but someone is gonna accidentally break that assumption. And even if they don't, you've now spent more time worrying about that then if you just did manual memory allocation.

Or, in C++ you write

    class Foo {
    public:
        Foo() {/*get handle*/}
        ~Foo() {/*release resource*/}
    };
and you never have to worry again.
Re:Why RAII rocks by falcolas on Friday April 24, 2015 @09:40AM (#9433180)
> You can move the resource allocation from __init__ to __enter__, but now you split your initialization code over 2 constructors

After giving it some thought, I've come up with a better example which more closely maps to C++ RAII constructs, which shows that you don't need to split anything. Consider:

    class Shadow(object):
        def __init__(self):
            # Initialization code
        @classmethod
        def __enter__(cls, *args, **kwargs):
            return cls(*args, **kwargs)
        def __exit__(self, *args, **kwargs):
            # De-initialize
`__enter__` becomes three lines of boilerplate, which you could abstract out into an inheritable class, should you so desire.

As for "In C++ you write", it's the same as saying "in Python you write __enter__ and __exit__", but instead of instantiating via `std::unique_ptr` you use `with`.

Re:Why RAII rocks by czinck on Friday April 24, 2015 @11:50AM (#9434136)
Huh, neat. I'd seen @classmethod a few times but wasn't sure what it did. I need to see if I can refactor some code now.
Re:Why RAII rocks by tstack on Thursday April 23, 2015 @05:32PM (#9430000)
Python has context managers, which were added for exactly this purpose.

  with alloc_resource() as resource:
      resource.use()
Re:Why RAII rocks by czinck on Thursday April 23, 2015 @05:42PM (#9430063)
I mentioned those. They work, but they're just a weak form of RAII without any extra power.
Re:Why RAII rocks by tstack on Friday April 24, 2015 @10:15AM (#9433410)
Can you elaborate on "extra power"? A c++ constructor/destructor pair is equivalent to __enter__ and __exit__. I fail to see how this grants a significant amount of power. There's certainly differences, but the gap is probably not as large as your weasel words make it out to be.

You complain about having to put initialization into 2 constructors, but there's the flip-side with things like mutexes. In c++, you have to have 2 classes, one for the lock and one for the guard. Whereas with a context-manager, you have one class and the locking code is in the __enter__ and __exit__.

    with self.lock:
        self.do_stuff()
In that case, I would say that the context-manager is nicer.
Re:Why RAII rocks by czinck on Friday April 24, 2015 @11:40AM (#9434071)
I didn't have any example of extra power, just in with blocks you have some downsides compared to RAII but no upsides. By downsides I mean that in C++ you write the constructor/destructor and never worry about it again, but in python every caller has to use a with block, plus what I said above about exceptions being thrown before __enter__.

I haven't done much with mutexes, but doesn't having it split over 2 classes introduce a race condition? Seems like a weird way to implement it to me.

Re:Why RAII rocks by EpicEng on Friday April 24, 2015 @02:47AM (#9431763)
Agree with @czinck. It works, it does the job, but it's not as fool proof as:

    {
        Type obj;
        // do stuff
    }
    // obj destructor just ran
C# and other languages have similar paradigms, but they require more attention to detail than a destructor does.
Re:Why RAII rocks by EugeneOZ on Thursday April 23, 2015 @06:03PM (#9430181)
then maybe you will be interested in PHP - it has good RAII and I use it in one lib for resources control and mutexes.