На самом деле, я предпочитаю немного другое решение. Дело в том, что результат внутреннего вызова не обязательно является допустимым результатом внешнего вызова. Например, внутренняя ошибка может быть «файл не найден», а внешняя «конфигурация недоступна». Поэтому я предлагаю воссоздать OpResult
(потенциально упаковывая в него "внутренний" OpResult для лучшей отладки). Это все идет в направлении «InnerException» в .NET.
технически, в моем случае макрос выглядит как
#define RETURN_IF_FAILED(call, outerresult) \
{ \
OpResult innerresult = call; \
if (innerresult.failed()) \
{ \
outerresult.setInner(innerresult); \
return outerresult; \
} \
}
Это решение требует управления памятью и т. Д.
Некоторые пуристы утверждают, что отсутствие явного возврата затрудняет читабельность кода. Однако, на мой взгляд, наличие явного RETURN
в качестве части имени макроса достаточно, чтобы предотвратить путаницу для любого опытного и внимательного разработчика.
Мое мнение таково, что такие макросы не запутывают логику программы, а наоборот делают ее чище. С таким макросом вы объявляете свое намерение ясным и кратким способом, в то время как другой способ кажется слишком многословным и поэтому подверженным ошибкам. Заставить сопровождающих анализировать, что та же самая конструкция OpResult r = call(); if (r.failed) return r
тратит их время.
Альтернативный подход без ранних возвратов заключается в применении к каждой строке кода шаблона, подобного CHECKEDCALL(r, call)
с #define CHECKEDCALL(r, call) do { if (r.succeeded) r = call; } while(false)
. На мой взгляд, это намного хуже и определенно подвержено ошибкам, так как люди, как правило, забывают добавить CHECKEDCALL()
при добавлении дополнительного кода.
Наличие популярной необходимости делать проверенные возвраты (или все) с макросами кажется мне небольшим признаком отсутствия языковой функции.