Насчёт освобождения ресурсов: в С++ используется
RAII (Получение ресурса есть инициализация), в более других языках (C#, Python), инструкция
with:
Код
#python
with closing(file('config.ini')) as cfg:
parse(cfg)
Здесь гарантируется, что при выходе из блока
with файл закроется. Можно написать свои обработчики входа/выхода из этого блока.
Неудобство с
finally в том, что получение ресурса и его освобождение разнесено в коде и отслеживается только программистом - соответственно можно просто забыть освободить, или спутать порядок освобождения - что иногда приводит к трудноотслеживаемым ошибкам.
RAII и wich этими болезнями не страдают.
Т.е. в этом разрезе, код будет выглядеть примерно так:
Код
#python
# какие-то действия 0 ..
with f1() as a:
# какие-то действия 1 ..
with f2() as b
# какие-то действия 2 ..
with f3() as c:
# какие-то действия 3 ..
Оказалась нераскрыта тема разных сообщений, по той пречине, что она может иметь несколько решений:
1. Рунтайм предоставляет нам полный трейс, значит мы и так довольно хорошо видим место ошибки и что к ней привело.
2. Каждое исключение содержит какое-то описание произошедшего, и нам его может оказаться вполне достаточно.
3. Я бы уже разбивал это на отдельные функции, например вынося туда "какие-то действия №Х .." и вложенный
with. В python-е есть очень удобное средство - декораторы - на них бы сделал разные сообщения ошибок.
Код
#python
@errlog('asd')
def part1():
# какие-то действия 0 ..
with f1() as a:
part2(a)
@errlog('qwe')
def part2(a):
# какие-то действия 1 ..
with f2() as b
part3(a, b)
...
И опять же, заметив, что все эти part№ вполне однотипны, можно их обобщить до:
Код
def do_part(before, acqure, after, *args):
before(*arg)
with acqure(*arg) as res:
after(res, *arg)
Конечно конкретный код будет несколько другой, но идея думаю понятна.
П.С. Вообще общая идея функциональной декомпозиции - каждая функция должна отвечать только за одно действие.
Тогда не будет каскада вложенных ифов и захватов/освобождений ресурсов, а сопровождать такие функции гораздо легче.