Навеяно, например,
Re: пример из книги: цикл + try/catchАвтор: Clevelus
Дата: 13.07.08
. Не буду затрагивать высокоуровневые вещи, флейм vc коды возврата, и TR 18015.
Рассмотрим 2 примера для самого плохого случая из промышленных компиляторов — MSVC x86 (32bit).
void foo(const string&);
void test_eh1(const vector<string> & v)
{
try
{
for ( vector<string>::const_iterator i = v.cbegin(); i != v.cend(); ++i )
foo(*i);
}
catch(...)
{
__asm int 3
}
}
void test_eh2(const vector<string> & v)
{
for ( vector<string>::const_iterator i = v.cbegin(); i != v.cend(); ++i )
{
try
{
foo(*i);
}
catch(...)
{
__asm int 3
}
}
}
Вот что создаёт компилятор, инструкции для поддержки C++ EH выделены:
_TEXT SEGMENT
__$EHRec$ = -16 ; size = 16
_v$ = 8 ; size = 4
?test_eh1@@YGXABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z PROC ; test_eh1, COMDAT
; 62 : {
push ebp
mov ebp, esp
push -1 ; состояние для обработчика SEH
;; установка SEH фрейма OS - регистрируется обработчик понимающий C++ исключения
push __ehhandler$?test_eh1@@YGXABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z
mov eax, DWORD PTR fs:0
push eax
mov DWORD PTR fs:0, esp
push ecx
push ebx
push esi
push edi
; 63 : try
; 64 : {
; 65 : for ( vector<string>::const_iterator i = v.cbegin(); i != v.cend(); ++i )
mov edi, DWORD PTR _v$[ebp]
mov esi, DWORD PTR [edi]
mov DWORD PTR __$EHRec$[ebp], esp
mov DWORD PTR __$EHRec$[ebp+12], 0 ; состояние для обработчика SEH
npad 5
$LL3@test_eh1:
cmp esi, DWORD PTR [edi+4]
je SHORT $LN10@test_eh1
; 66 : foo(*i);
push esi
call ?foo@@YGXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ; foo
add esi, 16 ; 00000010H
jmp SHORT $LL3@test_eh1
__catch$?test_eh1@@YGXABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z$0:
; 67 : }
; 68 : catch(...)
; 69 : {
; 70 : __asm int 3
int 3
; 71 : }
mov eax, $LN10@test_eh1
ret 0
$LN10@test_eh1:
; 72 : }
mov ecx, DWORD PTR __$EHRec$[ebp+4] ;; снатие SEH frame
pop edi
pop esi
mov DWORD PTR fs:0, ecx ;;
pop ebx
mov esp, ebp
pop ebp
ret 4
_TEXT ENDS
_TEXT SEGMENT
_i$21988 = -20 ; size = 4
__$EHRec$ = -16 ; size = 16
_v$ = 8 ; size = 4
?test_eh2@@YGXABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z PROC ; test_eh2, COMDAT
; 75 : {
push ebp
mov ebp, esp
push -1
push __ehhandler$?test_eh2@@YGXABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z
mov eax, DWORD PTR fs:0
push eax
mov DWORD PTR fs:0, esp
sub esp, 8
; 76 : for ( vector<string>::const_iterator i = v.cbegin(); i != v.cend(); ++i )
mov eax, DWORD PTR _v$[ebp]
push ebx
push esi
mov esi, DWORD PTR [eax]
push edi
mov DWORD PTR __$EHRec$[ebp], esp
$LN23@test_eh2:
mov ecx, DWORD PTR _v$[ebp]
mov DWORD PTR _i$21988[ebp], esi
cmp esi, DWORD PTR [ecx+4]
je SHORT $LN2@test_eh2
; 77 : {
; 78 : try
; 79 : {
; 80 : foo(*i);
push esi
mov DWORD PTR __$EHRec$[ebp+12], 0
call ?foo@@YGXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ; foo
mov DWORD PTR __$EHRec$[ebp+12], -1
; 76 : for ( vector<string>::const_iterator i = v.cbegin(); i != v.cend(); ++i )
add esi, 16 ; 00000010H
jmp SHORT $LN23@test_eh2
__catch$?test_eh2@@YGXABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z$0:
; 81 : }
; 82 : catch(...)
; 83 : {
; 84 : __asm int 3
int 3
; 85 : }
mov DWORD PTR __$EHRec$[ebp+12], -1
mov eax, $LN21@test_eh2
ret 0
$LN21@test_eh2:
mov esi, DWORD PTR _i$21988[ebp]
add esi, 16 ; 00000010H
jmp SHORT $LN23@test_eh2
$LN2@test_eh2:
; 86 : }
; 87 : }
mov ecx, DWORD PTR __$EHRec$[ebp+4]
pop edi
pop esi
mov DWORD PTR fs:0, ecx
pop ebx
mov esp, ebp
pop ebp
ret 4
_TEXT ENDS
Если откинуть мусор, останется соль — команды вида
mov DWORD PTR __$EHRec$[ebp+12], 0
mov DWORD PTR __$EHRec$[ebp+12], -1
Это неявно вводимая компилятором переменная (типа ehstate) — состояние, по которому, в случае выброса исключения, обработчик определит, где оно произошло. "Оверхед" от этих команд очень мал по сравнению, например, с вызовом функции — на скорости цикла не скажется.
SEH фрейм регистрируется один раз — в прологе и снимается один раз — в эпилоге функции. В этом примере не видно, но можете проверять сами — картина останется неизменной независимо от количества вложенных try catch блоков. Будет лишь меняться значение ehstate — по сути, уровень вложенности. Если интересно, как работает обработчик, можно почитать статью Matt Pitreck в MSDN (есть перевод на wasm.ru) об обработке обычных (SEH) исключений компилятором MSVC. C++ EH работает примерно так же, только структура на стеке чуть меньше
Из принципиальных отличий от кода без исключений, я бы выделил невозможность инлайна:
1. дестракторов объектов из try блока;
2. самих функций с try блоком.
но тут еще вопрос, является ли это оверхедом, учитывая, что функция вызывается более одного раза...
Вывод такой — не стоит забивать голову ерундой об оверхеде. Нужно быть очень увереннымв себе, что бы писать под Win даже без __try __except, а C++ EH лишнего практически не вносит. Разве что копирование выброшенного объекта, если используется в обработчике (кстати, при ловле по ссылке — будет скопирована "ссылка"). А в x64 битной Win и SEH бесплатен.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth