An excellent question!
But first, why C sucks
There are many reasons why C sucks but they mostly all boil down to the fact that C doesn't have a destructor.
C++ has destructors and as a result C++ is way better.
If you're not familiar with destructors, they're a function that is automatically run when a variable goes out of scope and is about to be deleted.
Even if there is an exception.
This is a very powerful concept.
Speaking of powerful concepts, does everybody know what RAII is?
It's a crap name.
Mostly a C++ pattern since C++ is one of the few languages to have a destructor.
So that means...
an object aquires a resource when created
then gives it back when deleted
For example...
create a database connection and then close the connection
open a file, and then close the file
It boils down to needing to undo something whenever you do something. A LIFO queue or stack if you're into compsci terms.
FILE* f = fopen("some_file.txt", "r");
if( f != NULL )
{
// read the file and do stuff
fclose(f);
}
FILE* f = fopen("some_file.txt", "r");
if( f != NULL )
{
// read the file and do stuff, what if this throws an exception?
fclose(f);
}
FILE* f = fopen("some_file.txt", "r");
if( f != NULL )
{
// read the file and do stuff, what if this throws an exception?
fclose(f); // will this get called?
}
FILE* f = fopen("some_file.txt", "r");
if( f != NULL )
{
// read the file and do stuff, what if this throws an exception?
fclose(f); // will this get called? No. No it won't.
}
f = open("some_file.txt","r")
f.read()
close(f)
It looks a lot like the C version.
try:
f = open("some_file.txt","r")
f.read()
finally:
close(f)
This is actually totally safe but verbose and hard to read.
with open("some_file.txt","r") as f:
f.read()
# or throw an exception, we don't care
# close(f) will get called
this more or less discombobulates to the previous example
Anybody who's every done multithreaded code knows you need to use locks/mutex/semaphores/critical sections/etc
Hopefully you also know that you have to unlock your mutex when you're done.
from threading import Lock
mutex = Lock()
mutex.acquire()
print "is locked: ", mutex.locked()
mutex.release()
print "is locked: ", mutex.locked()
is locked: True
is locked: False
from threading import Lock
mutex = Lock()
with mutex:
print "is locked: ", mutex.locked()
print "is locked: ", mutex.locked()
is locked: True
is locked: False
Two methods...
from contextlib import contextmanager
@contextmanager
def custom_ctx_mgr(*args, **kwargs):
# setup code
yield # can return something if desired
# cleanup code
class custom_ctx_mgr(object):
def __init__(self, *args, **kwargs):
"""
this is a normal object and can take initialization parameters
"""
def __enter__(self):
"""
the value returned by this method is bound to the `as` variable
"""
return self # or other object or nothing
def __exit__( self, exc_type, exc_val, exc_traceback):
"""
return True to cause caller to continue happily
return False to cause caller to reraise passed in exception
"""
with a_ctx_mgr():
# do some stuff
with a_ctx_mgr() as var:
# do some stuff with var
you can nest them
with a_ctx_mgr() as var:
with other_ctx_mgr() as other_var:
# do some stuff with var and other_var
or on the same line
with a_ctx_mgr() as var, with other_ctx_mgr() as other_var:
# do some stuff with var and other_var
function is too big and ugly to fit here
To know them is to love them.
To love them is to know them.