Моя мысль заключается в следующем.Пожалуйста, имейте в виду, что я пишу весь код для этого на лету, чтобы он не был идеальным.:)
На самом деле это, вероятно, легче объяснить в коде, но я сначала попытаюсь дать суть.Поскольку вы не заинтересованы в catch (...)
, я не сосредоточился на обнаружении этого, однако, я думаю, что было бы относительно легко изменить идею, чтобы справиться и с этим.(Примечание: изначально я собирался использовать указатель на функцию как способ сказать, в какой функции вы находитесь, но в итоге я выбрал имя, потому что не думал о виртуальных функциях. Я уверен, что это можетвсе они должны быть оптимизированы, если необходимо.)
Создайте следующее:
- класс со статическими версиями ваших желаемых функций
- специальный пользовательский тип "стека" дляхранить информацию об исключении с операциями, которые могут разрушить стек, основываясь на этой информации
- структура, содержащая строки типа и строку void pointer для хранения имени функции, в которой она была создана
Настройка:
- до вашего
try
, поместите структуру типа catch с именем перехваченного типа, а также со всеми исключениями, перехваченными в этой функции, настек вместе с указателем на имя функции. - строка для типа определяется путем цитирования типа (так что
"..."
подходит для значения по умолчаниют ловит). - Первоначально я играл с идеей использовать
typeid
для получения недокорированных имен типов, затем использовать .raw_name()
для получения искаженного имени - , но это не будет работать для нативныхтипы, или для не виртуальных типов, и в действительности нет необходимости в искаженных именах, так что это немного бессмысленно для этой реализации
Разрыв:
- в каждом
catch
блоке, наверху сорвите стек на единицу больше, чем тот, который вы перехватываете для функции, в которой вы находитесь - после последних
catch
блоков в функции, порвитесложите один после первого случая разрыва в последнем улове
Основная проблема этого решения заключается в том, что оно явно очень громоздко.
Одним из решений является [поспорить, что вы видели это прибывающее] макросы.
ExceptionStackHandler.h
// ...
// declaration of the class with the needed functions, perhaps
// inline definitions. the declaration of the stack. etc.
// ...
#if __STDC__ && __STDC_VERSION__ >= 199901L
#define FN_NAME __func__
#else
#define FN_NAME __FUNCTION__
#endif
// was thinking would be more to this; don't think we need it
//#define try_code(code) try { code }
// this macro wraps the code such that expansion is not aborted
// if there happen to be commas in the code.
#define protect(code) if (true) { code }
// normal catch and processing
#define catch_code(seed_code, catch_type, catch_code) \
ExceptionStackHandler.Stack.Push(exceptionItem(#catch_type, FN_NAME)); \
seed_code \
catch (catch_type Ex) \
{ \
ExceptionStackHandler.Stack.PopThrough(#catch_type, FN_NAME); \
catch_code \
}
// you *must* close a try with one of the following two calls, otherwise
// some items may be missed when clearing out the stack
// catch of all remaining types
#define close_catchall(seed_code, last_catch_type, catch_code) \
seed_code \
catch (...) \
{ \
ExceptionStackHandler.Stack.PopThrough(#last_catch_type, FN_NAME); \
catch_code \
} \
ExceptionStackHandler.Stack.PopThrough(#last_catch_type, FN_NAME); \
// cleanup of code without catching remaining types
#define close_nocatch(last_catch_type, catch_code) \
seed_code \
ExceptionStackHandler.Stack.PopThrough(#last_catch_type, FN_NAME)
Тогда в вашем коде это будет выглядеть как
bool isTheRoofOnFire(bool& isWaterNeeded)
{
// light some matches, drip some kerosene, drop a zippo
close_nocatch
(
catch_code
(
catch_code
(
//--------------------------------------------------------
// try block for the roof
try
{
protect (
// we don't need no water
if (isWaterNeeded)
isWaterNeeded = false;
)
}
// End Try Block
//--------------------------------------------------------
,
//--------------------------------------------------------
// catch(string Ex)
string,
protect (
if (Ex == "Don't let it burn!")
isWaterNeed = true;
throw "I put the water on the fire anyway.";
)
)
// END - catch (string Ex)
//--------------------------------------------------------
,
//--------------------------------------------------------
// catch(RoofCollapsedException Ex)
RoofCollapsedException
try_code (
protect (
if (RoofCollapsedException.isAnythingWeCanDo == false)
throw new TooLateException(RoofCollapsedException);
else
isWaterNeeded = true;
)
)
// END - catch(RoofCollapsedException Ex)
//--------------------------------------------------------
)
// closing without catchall exception handler
//--------------------------------------------------------
}
Теперь, я признаю, это ужасно.Reeeal некрасиво.Я уверен, что есть лучший способ написать эти макросы, но как теоретическое подтверждение концепции, я не думаю, что там есть что-то, что не работает.Но настоящее решение не может быть таким сложным.Причина, по которой это не получается, не в том, что идея такая безобразная.Просто макросы не могут реализовать это чистым способом.Поскольку это такой обычный шаблон, просто должен быть способ сделать это, даже не касаясь вашего исходного кода.Если бы только препроцессор C не был единственным выбором ...
;) Итак.На самом деле я думаю, что может быть.Лучшим решением является использование более мощного препроцессора, который дает более чистый код C ++, позволяя вам компилировать даже без дополнительной предварительной обработки (например, директивы в качестве комментариев).Я думаю, что было бы довольно легко написать что-нибудь, используя инструмент, подобный CS-Script (который будет работать под Mono), и я полагаю, что некоторые примеры включены в документацию процесса 'прекомпилятора', который позволяетты делаешь это.И действительно, для этого: вам даже не нужны директивы.Директивы были бы хороши, но вам не нужен универсальный макропроцессор, чтобы делать то, что вам нужно.Конечно, само собой разумеется, что вы можете написать это во всем, что имеет возможность обрабатывать текстовый файл.
Хотя я еще не пытался реализовать его, я думаю, что это может быть просто процессор, который работает на целой группе файлов, которые не требуют каких-либо изменений непосредственно в коде. (Найдите все блоки try/catch
в файле, соберите типы, создайте дополнительные операторы и запишите файл.) Возможно, переместите каталог, из которого Makefile извлекает файлы сборки, затем перед компиляцией обработайте все файлы и поместите вывод в новый подкаталог сборки. Могу поспорить, что LINQ может сделать это в нескольких небольших заявлениях, хотя это не означает, что I может написать LINQ. :) Я все еще держу пари, что это не будет такой большой задачей, и это будет идеальный способ реализовать решение; определить стек, класс и функции статической проверки в классе.
Что мне напоминает ... за "полноту":
bool ExceptionStackHandling::isThereACatchBlock(string Type)
{
return (ExceptionStackHandling.Stack.peekOnType(Type) > 0);
}
Итак, в заключение: я с трудом могу себе представить, что мы имеем дело с кодом, подобным коду, который у вас получается с макросами. Теперь я не делал отступ, и полагаю, что он станет почти наполовину более читабельным, но проблема только что переместилась: теперь, если вы обрабатываете семь типов исключений, у вас есть семь отступов, выталкивающих все с экрана. Но я думаю, что что-то можно сделать с помощью простого внешнего приложения, которое делает все это автоматически для вас.