Типичная ситуация: функция верхнего уровня вызывает функцию более низкого уровня, та — следующую и т.п., и где-то на N-м уровне очередной вызов возвращает ошибку. Если это что-то уникальное, то можно вернуть наверх (хоть через обычные return, хоть через исключения) уникальный код и/или описание. Но если ошибка общего характера (нехватка ресурсов, отказ в доступе, разрыв соединения и т.п.), то неплохо бы вернуть наверх более подробную информацию, которую можно показать пользователю, чтобы он имел более-менее адекватное представление о проблеме.
Например, в винде издавна принято возвращать отовсюду HRESULT. В результате, например, Windows Update при любой проблеме тупо выдает "failed" вместе с HRESULT, и даже по логам не сразу определишь, что и где обломалось. А программы на фреймворках типа .NET, наоборот, могут столь же тупо вывалить пользователю раскрутку стека, из которой он тоже мало что поймет, и которую таки лучше писать в лог. Хочется выдавать нечто промежуточное — не абстрактный "access denied", но и не историю вложенных вызовов в чистом виде.
Возникает соблазн завести какой-нибудь универсальный класс Result, и в процессе возврата изнутри наружу добавлять в объект уточнения, если необходимо. Но если возвращать его обычным образом, через return, то RVO/NRVO в отладочном режиме [практически] не используется, возвращаемые объекты (где будут минимум сотни байт) будут многократно копироваться, а функции нижнего уровня могут вызываться и сотни-тысячи раз в секунду. По той же причине не всегда годятся исключения — функции нижнего уровня не всегда возвращают ошибки, фатальные для верхних уровней.
Другой вариант — использовать TLS, как в винде для GetLastError. А как с этим в Linix/MacOS/Android/iOS?
Вообще, какие способы возврата уточненных результатов из многократно вложенных вызовов ныне считаются кошерными?