Компилятор захочет вставить в место инлайна этой функции SEH-фрейм, по причине того, что он предполагает что функции AddRef и Release могут кидать исключения. Особенно досадно это видеть в деструкторах, которые делают Release для интерфейсов. Добавив в пару широко используемых функций (например в smart pointer, который делает в конструкторе AddRef, а в деструкторе Release), спецификатор __declspec(nothrow) (этот спецификатор убирает SEH-фрейм в данной функции и считает, что эта функция не кидает исключения) я получил существенный выигрыш по объему сгенерированного кода (модуль размером 2.7 мб уменьшился на 6%). Но писать этот спецификатор по месту использования не слишком рационально, хотелось бы пометить сами интерфейсы как не кидающие.
Попробовал несколько способов достижения желаемого эффекта, но они меня все не удовлетворили. Перечислю их ниже (для простоты я оставил только метод Release):
Этот код не приносит ожидаемого эффекта. MSVC все равно думает, что этот метод может кинуть.
Т.к. MSVC считает (если компилировать с флагом /EHsc), что функции, объявленные как extern "C" никогда не кидают, была сделана попытка поместить интерфейс в extern "C". Но требуемого поведения, все равно не получилось достигнуть.
Возможно, кто-нибудь знает способ как можно заставить MSVC 10 думать, что виртуальный метод не кидает исключений? Тогда бы он догадался не делать лишние SEH-фреймы и стал бы лучше генерировать код.
Спасибо.
Re: Как убедить MSVC 10 в том, что виртуальная функция не ки
__declspec(nothrow) говорит о том, что ты обещаешь не выбрасывать исключения с помощью throw. Но это синхронные исключения, а гарантировать, что не будет SEH-исключений, ты не можешь. Деление на 0 — и пожалуйста. И что потом делать ?
PD>__declspec(nothrow) говорит о том, что ты обещаешь не выбрасывать исключения с помощью throw. Но это синхронные исключения, а гарантировать, что не будет SEH-исключений, ты не можешь. Деление на 0 — и пожалуйста. И что потом делать ?
Очевидно, падать, т.к. опции компиляции Ehsc. А разве кто-то ожидал другого?
С т.з. VC __declspec(nothrow) говорит не о том, что функция не будет выбрасывать исключений для внешнего кода (что можно проверить, указав это для функции и убедиться, что код обработки исключений из внешнего кода не исчезает), а о том, что внутри функции не выбрасываются исключения, если иное не следует явно. И внутри функции, промаркированной как throw() как раз код обработки исключений в этом случае отсутствует. gcc же, напротив, по спецификатору noexcept считает во внешнем коде, что вызываемая функция не кидает исключений и убирает обработчик из внешнего кода.
Здравствуйте, Andrew S, Вы писали:
AS>С т.з. VC __declspec(nothrow) говорит не о том, что функция не будет выбрасывать исключений для внешнего кода (что можно проверить, указав это для функции и убедиться, что код обработки исключений из внешнего кода не исчезает), а о том, что внутри функции не выбрасываются исключения, если иное не следует явно. И внутри функции, промаркированной как throw() как раз код обработки исключений в этом случае отсутствует.
Да, ты прав. Но это не отменяет того, что я сказал — выбрасывать SEH исключения она все равно может.
>gcc же, напротив, по спецификатору noexcept считает во внешнем коде, что вызываемая функция не кидает исключений и убирает обработчик из внешнего кода.
With best regards
Pavel Dvorkin
Re[4]: Как убедить MSVC 10 в том, что виртуальная функция не ки
AS>>С т.з. VC __declspec(nothrow) говорит не о том, что функция не будет выбрасывать исключений для внешнего кода (что можно проверить, указав это для функции и убедиться, что код обработки исключений из внешнего кода не исчезает), а о том, что внутри функции не выбрасываются исключения, если иное не следует явно. И внутри функции, промаркированной как throw() как раз код обработки исключений в этом случае отсутствует.
PD>Да, ты прав. Но это не отменяет того, что я сказал — выбрасывать SEH исключения она все равно может.
Так вопрос то в другом — как убедить компилятор, что C++ исключений быть не может и SEH фрейм, который он делает только для этого, тут не нужен.
>>gcc же, напротив, по спецификатору noexcept считает во внешнем коде, что вызываемая функция не кидает исключений и убирает обработчик из внешнего кода.
На правах гипотезы — возможно, визуальник защищается не от того, что функция кинет, а от разыменовывания нулевого указателя. Может, если там будет, например, ссылка — ему будет легче?
Или __assume (obj != 0). Только обратите внимание что __assume с неправильным условием крайне опасен.
Re[2]: Как убедить MSVC 10 в том, что виртуальная функция не кидает исключений?
Здравствуйте, Pretender, Вы писали:
P>На правах гипотезы — возможно, визуальник защищается не от того, что функция кинет, а от разыменовывания нулевого указателя. Может, если там будет, например, ссылка — ему будет легче?
Нет, он защищается не от разыменования нулевого указателя. И защищаться он от этого не должен: это undefined behaviour.
P>Или __assume (obj != 0). Только обратите внимание что __assume с неправильным условием крайне опасен.
Это не помогает. Но, спасибо, что сказали о наличии __assume, не знал об этом.
Re[5]: Как убедить MSVC 10 в том, что виртуальная функция не ки
AS>>>С т.з. VC __declspec(nothrow) говорит не о том, что функция не будет выбрасывать исключений для внешнего кода (что можно проверить, указав это для функции и убедиться, что код обработки исключений из внешнего кода не исчезает), а о том, что внутри функции не выбрасываются исключения, если иное не следует явно. И внутри функции, промаркированной как throw() как раз код обработки исключений в этом случае отсутствует.
PD>>Да, ты прав. Но это не отменяет того, что я сказал — выбрасывать SEH исключения она все равно может.
AS>Так вопрос то в другом — как убедить компилятор, что C++ исключений быть не может и SEH фрейм, который он делает только для этого, тут не нужен.
Поправлюсь — VC на самом деле учитывает и внешнй скоп для throw() функций. Но только не для виртуальных. Для них он делает seh frame вне зависимости от их спецификации исключений/наличия прагмы. Похоже, ms не очень заботится о размере COM-овского и ему подобного кода .
volatile нужен для того, чтобы функция из IInterface была не виртуальной (скорее всего, можно заменить volatile на const). В этом случае компилятор поймет аттрибут __declspec(nothrow) и сделает как надо.
На первый взгляд решение выглядит более-менее. Может быть, я что-нибудь не учел?
Здравствуйте, Maxim Yurchuk, Вы писали:
MY>Похоже я нашел один workaround, который обладает обратной совместимостью (в моем случае) и позволяет генерировать код, который мне хочется.
MY>Достаточно заменить интерфейс
MY>
MY>volatile нужен для того, чтобы функция из IInterface была не виртуальной (скорее всего, можно заменить volatile на const). В этом случае компилятор поймет аттрибут __declspec(nothrow) и сделает как надо.
MY>На первый взгляд решение выглядит более-менее. Может быть, я что-нибудь не учел?
У тебя тут получаются 2 разные функции Foo(). Одна не-'volatile' и виртуальная, другая 'volatile' и не виртуальная.
Получается, что по указателю на баз.класс ты не вызовешь свою производную функцию.
Тебе разве такое поведение нужно?
Здравствуйте, Maxim Yurchuk, Вы писали:
MY>volatile нужен для того, чтобы функция из IInterface была не виртуальной (скорее всего, можно заменить volatile на const). В этом случае компилятор поймет аттрибут __declspec(nothrow) и сделает как надо.
MY>На первый взгляд решение выглядит более-менее. Может быть, я что-нибудь не учел?
я бы остерёгся volatile, может лучше добавить лишний задефолченый параметр?
Здравствуйте, sizeof_void, Вы писали:
_>У тебя тут получаются 2 разные функции Foo(). Одна не-'volatile' и виртуальная, другая 'volatile' и не виртуальная. _>Получается, что по указателю на баз.класс ты не вызовешь свою производную функцию. _>Тебе разве такое поведение нужно?
Через указатель на IInterface вызывается сначала не виртуальная функция, которая, в свою очередь, вызывает виртуальную функцию, которая реализована в производном классе (не в интерфейсном). За счет того, что сначала вызывается не виртуальная функция с __declspec(nothrow) компилятор понимает, что интерфейс не кидает исключений.